diff --git a/.github/workflows/clf.yml b/.github/workflows/clf.yml index f2d0a339..2cb6808f 100644 --- a/.github/workflows/clf.yml +++ b/.github/workflows/clf.yml @@ -1,4 +1,4 @@ -name: autobuild_clf +name: ci_classification on: pull_request: branches: @@ -9,42 +9,57 @@ on: - master - 'V**' - dev + env: GDRIVE_CREDENTIALS_DATA: ${{ secrets.GDRIVE_CREDENTIALS_DATA }} REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} jobs: - build: + test-classification: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - name: Set up Python 3.10 - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 with: - python-version: '3.10' - architecture: 'x64' - - uses: actions/setup-node@v3 + python-version: '3.11' + + - name: Install uv + uses: astral-sh/setup-uv@v4 + + - uses: actions/setup-node@v4 with: - node-version: '16' - - uses: iterative/setup-cml@v1 - - name: Display Python version - run: python -c "import sys; print(sys.version)" + node-version: '20' + + - uses: iterative/setup-cml@v3 + + - name: Cache uv packages + uses: actions/cache@v4 + with: + path: ~/.cache/uv + key: ${{ runner.os }}-uv-clf-${{ hashFiles('pyproject.toml') }} + restore-keys: | + ${{ runner.os }}-uv-clf- + - name: Install dependencies run: | - python -m pip install --upgrade pip - pip install torch torchvision --extra-index-url https://download.pytorch.org/whl/cpu - pip install -e '.[dev,cv,cv_classification]' - pip install dvc dvc-gdrive + uv pip install --system torch torchvision --index-url https://download.pytorch.org/whl/cpu + uv pip install --system -e '.[dev,cv,cv_classification]' + uv pip install --system dvc dvc-gdrive npm install -g markdown-table-cli + - name: Download data run: | echo "$GDRIVE_CREDENTIALS_DATA" > credentials.json dvc remote modify gdrive --local gdrive_user_credentials_file ./credentials.json dvc pull ls -la samples + - name: Test with pytest run: | - pytest tests/classification --capture=no + uv run pytest tests/classification --capture=no + - name: Display report run: | echo "# Classification Report" > report.md @@ -53,7 +68,7 @@ jobs: echo "#### Confusion Matrix" >> report.md echo "![Confusion Matrix](runs/pytest_clf/Validation/cfm.png)" >> report.md echo "#### Errorcases" >> report.md - echo "![Confusion Matrix](runs/pytest_clf/Validation/errorcases.png)" >> report.md + echo "![Errorcases](runs/pytest_clf/Validation/errorcases.png)" >> report.md echo "------------------" >> report.md echo "#### Hyperparameters Tuning" >> report.md echo "Leaderboard" >> report.md diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 9fd896b5..bd5afb47 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -1,4 +1,4 @@ -name: autobuild_docker +name: ci_docker on: pull_request: branches: @@ -10,26 +10,31 @@ env: DOCKERHUB_TAG: kaylode/theseus:latest jobs: - run: + build-and-test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - name: Set up Python 3.10 - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 with: - python-version: '3.10' - architecture: 'x64' - - uses: iterative/setup-cml@v1 + python-version: '3.11' + + - uses: iterative/setup-cml@v3 - uses: iterative/setup-dvc@v1 + - name: Set up QEMU - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@v3 + - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 + - name: Create credentials file run: | echo "$GDRIVE_CREDENTIALS_DATA" > credentials.json + - name: Build Dockerfile - uses: docker/build-push-action@v4 + uses: docker/build-push-action@v6 with: context: . tags: ${{ env.DOCKERHUB_TAG }} @@ -40,10 +45,10 @@ jobs: - name: Free up space run: | docker system prune -f - sudo rm -rf "/usr/local/share/boost" + sudo rm -rf /usr/local/share/boost sudo rm -rf "$AGENT_TOOLSDIRECTORY" - - name: Docker name + - name: Load Docker image run: | docker load -i ${{ github.workspace }}/image.tar docker image inspect ${{ env.DOCKERHUB_TAG }} @@ -61,16 +66,16 @@ jobs: pytest tests --capture=no - name: Log in to Docker Hub - uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Push image to DockerHub run: | - docker push --all-tags + docker push ${{ env.DOCKERHUB_TAG }} - - name: where am I + - name: Debug workspace run: | pwd ls -la diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 00000000..a2c47a5b --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,36 @@ +name: lint +on: + pull_request: + branches: + - master + - dev + push: + branches: + - master + - dev + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Install uv + uses: astral-sh/setup-uv@v4 + + - name: Install dependencies + run: | + uv pip install --system ruff + + - name: Ruff check + run: | + ruff check theseus/ tests/ + + - name: Ruff format check + run: | + ruff format --check theseus/ tests/ diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..f2239b37 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,39 @@ +name: release +on: + push: + tags: + - 'v*' + +permissions: + contents: read + id-token: write + +jobs: + publish: + runs-on: ubuntu-latest + environment: + name: pypi + url: https://pypi.org/p/theseus + steps: + - uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Install uv + uses: astral-sh/setup-uv@v4 + + - name: Install build dependencies + run: | + uv pip install --system build + + - name: Build package + run: | + python -m build + + - name: Publish to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + skip-existing: true diff --git a/.github/workflows/segm.yml b/.github/workflows/segm.yml index 581bcb92..d3d8089a 100644 --- a/.github/workflows/segm.yml +++ b/.github/workflows/segm.yml @@ -1,4 +1,4 @@ -name: autobuild_segm +name: ci_segmentation on: pull_request: branches: @@ -9,42 +9,57 @@ on: - master - 'V**' - dev + env: GDRIVE_CREDENTIALS_DATA: ${{ secrets.GDRIVE_CREDENTIALS_DATA }} REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} jobs: - build: + test-segmentation: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - name: Set up Python 3.10 - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 with: - python-version: '3.10' - architecture: 'x64' - - uses: actions/setup-node@v3 + python-version: '3.11' + + - name: Install uv + uses: astral-sh/setup-uv@v4 + + - uses: actions/setup-node@v4 with: - node-version: '16' - - uses: iterative/setup-cml@v1 - - name: Display Python version - run: python -c "import sys; print(sys.version)" + node-version: '20' + + - uses: iterative/setup-cml@v3 + + - name: Cache uv packages + uses: actions/cache@v4 + with: + path: ~/.cache/uv + key: ${{ runner.os }}-uv-segm-${{ hashFiles('pyproject.toml') }} + restore-keys: | + ${{ runner.os }}-uv-segm- + - name: Install dependencies run: | - python -m pip install --upgrade pip - pip install torch torchvision --extra-index-url https://download.pytorch.org/whl/cpu - pip install -e '.[dev,cv,cv_semantic]' - pip install dvc dvc-gdrive + uv pip install --system torch torchvision --index-url https://download.pytorch.org/whl/cpu + uv pip install --system -e '.[dev,cv,cv_semantic]' + uv pip install --system dvc dvc-gdrive npm install -g markdown-table-cli + - name: Download data run: | echo "$GDRIVE_CREDENTIALS_DATA" > credentials.json dvc remote modify gdrive --local gdrive_user_credentials_file ./credentials.json dvc pull ls -la samples + - name: Test with pytest run: | - pytest tests/semantic --capture=no + uv run pytest tests/semantic --capture=no + - name: Display report run: | echo "# Semantic Report" > report.md diff --git a/.github/workflows/tablr.yml b/.github/workflows/tablr.yml index 734e6575..3ff478d4 100644 --- a/.github/workflows/tablr.yml +++ b/.github/workflows/tablr.yml @@ -1,4 +1,4 @@ -name: autobuild_tablr +name: ci_tabular on: pull_request: branches: @@ -9,42 +9,57 @@ on: - master - 'V**' - dev + env: GDRIVE_CREDENTIALS_DATA: ${{ secrets.GDRIVE_CREDENTIALS_DATA }} REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} jobs: - build: + test-tabular: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - name: Set up Python 3.10 - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 with: - python-version: '3.10' - architecture: 'x64' - - uses: actions/setup-node@v3 + python-version: '3.11' + + - name: Install uv + uses: astral-sh/setup-uv@v4 + + - uses: actions/setup-node@v4 with: - node-version: '16' - - uses: iterative/setup-cml@v1 - - name: Display Python version - run: python -c "import sys; print(sys.version)" + node-version: '20' + + - uses: iterative/setup-cml@v3 + + - name: Cache uv packages + uses: actions/cache@v4 + with: + path: ~/.cache/uv + key: ${{ runner.os }}-uv-tablr-${{ hashFiles('pyproject.toml') }} + restore-keys: | + ${{ runner.os }}-uv-tablr- + - name: Install dependencies run: | - python -m pip install --upgrade pip - pip install torch torchvision --extra-index-url https://download.pytorch.org/whl/cpu - pip install -e '.[dev,ml]' - pip install dvc dvc-gdrive + uv pip install --system torch torchvision --index-url https://download.pytorch.org/whl/cpu + uv pip install --system -e '.[dev,ml]' + uv pip install --system dvc dvc-gdrive npm install -g markdown-table-cli + - name: Download data run: | echo "$GDRIVE_CREDENTIALS_DATA" > credentials.json dvc remote modify gdrive --local gdrive_user_credentials_file ./credentials.json dvc pull ls -la samples + - name: Test with pytest run: | - pytest tests/tabular --capture=no + uv run pytest tests/tabular --capture=no + - name: Display report run: | echo "# Tabular Classification Report" > report.md diff --git a/.gitignore b/.gitignore index afeb501a..c14f529a 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ catboost_info/ .cache /build/ credentials.json +.ruff_cache/ +.venv/ \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index fdae9e4f..ad727569 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,17 +1,15 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.3.0 + rev: v4.6.0 hooks: - id: check-yaml - id: end-of-file-fixer - id: trailing-whitespace -- repo: https://github.com/pycqa/isort - rev: 5.12.0 + - id: check-added-large-files + args: ['--maxkb=1000'] +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.5.0 hooks: - - id: isort - args: ["--profile", "black"] -- repo: https://github.com/python/black.git - rev: 22.12.0 - hooks: - - id: black - language_version: python3 + - id: ruff + args: [--fix] + - id: ruff-format diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..86c7f085 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,57 @@ +# AGENT.md — Theseus Development Guide + +## Project Overview +Theseus is a PyTorch Lightning framework for ML/DL training. It provides a modular, OOP-heavy architecture with registries for models, datasets, losses, metrics, callbacks, trainers, and augmentations. Supports computer vision (classification, detection, segmentation), NLP, and tabular ML tasks. + +## Architecture +- **Registry Pattern**: All components registered via `Registry` class, resolved from YAML configs at runtime +- **Pipeline Pattern**: `BasePipeline` orchestrates init of all components (globals → registry → data → model → callbacks → trainer) +- **Lightning Wrappers**: `LightningModelWrapper` (pl.LightningModule) and `LightningDataModuleWrapper` (pl.LightningDataModule) bridge custom components to Lightning +- **Observer Logger**: `LoggerObserver` uses subscriber pattern for logging to stdout, files, TensorBoard, W&B +- **Config-driven**: Hydra + OmegaConf for all configuration + +## Directory Structure +``` +theseus/ +├── base/ # Core abstractions (pipeline, models, datasets, losses, metrics, callbacks, trainer, utilities) +├── cv/ # Computer vision tasks (classification, detection, semantic segmentation) +├── ml/ # Traditional ML (tabular, XGBoost, LightGBM, CatBoost, SHAP) +├── nlp/ # NLP tasks (base, retrieval) +├── registry.py # Central Registry class +tests/ # Pytest suites for classification, semantic, tabular +.github/workflows/ # CI pipelines (clf, segm, tablr, docker, lint, release) +``` + +## Development Conventions +- **OOP-heavy**: Prefer class hierarchies and inheritance. Use `abc.ABC` for abstract bases. +- **Registry-first**: All components must register with appropriate Registry +- **Config-driven instantiation**: Use `get_instance()` / `get_instance_recursively()` from Hydra configs +- **Type hints**: All public APIs must have type annotations +- **Formatting**: `ruff` for linting and formatting (replaces black + isort) +- **Package manager**: `uv` (not pip) +- **Testing**: `pytest` with `pytest-order` for ordered test execution + +## Key Commands +```bash +# Install +uv sync --all-extras + +# Run tests +uv run pytest tests/ --capture=no + +# Lint +uv run ruff check theseus/ +uv run ruff format theseus/ + +# Train (example) +uv run train.py --config-dir configs --config-name pipeline.yaml +``` + +## Current Progress (v2.0 Update) +- [x] Created `v2.0-update` branch from `dev` +- [x] Created this `AGENT.md` +- [x] Modernize dependencies in `pyproject.toml` +- [x] Update core framework (Registry, Pipeline, Wrapper, Logger) +- [x] Add HuggingFace Hub integration +- [x] Modernize GitHub workflows +- [x] Verify all changes diff --git a/Dockerfile b/Dockerfile index 15f7eb65..4fdbc491 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,17 +1,17 @@ -# syntax = docker/dockerfile:experimental +# syntax = docker/dockerfile:1 # -# NOTE: To build this you will need a docker version > 18.06 with -# experimental enabled and DOCKER_BUILDKIT=1 +# Multi-stage Dockerfile for Theseus v2.0 +# Requires Docker BuildKit (DOCKER_BUILDKIT=1) # -# If you do not use buildkit you are not going to have a good time -# -# For reference: -# https://docs.docker.com/develop/develop-images/build_enhancements/ -ARG BASE_IMAGE=ubuntu:18.04 +ARG BASE_IMAGE=nvidia/cuda:12.4.1-devel-ubuntu22.04 + +# ============================================================================= +# Stage 1: System dependencies +# ============================================================================= +FROM ${BASE_IMAGE} AS base -# Instal basic utilities -FROM ${BASE_IMAGE} as dev-base -RUN apt-get clean && apt-get update && apt-get upgrade && apt-get install -y --no-install-recommends \ +ENV DEBIAN_FRONTEND=noninteractive +RUN apt-get update && apt-get install -y --no-install-recommends \ build-essential \ ca-certificates \ ccache \ @@ -22,49 +22,53 @@ RUN apt-get clean && apt-get update && apt-get upgrade && apt-get install -y -- wget \ libjpeg-dev \ zip \ - swig python3-dev \ - unzip bzip2 ffmpeg libsm6 libxext6 \ - libpng-dev && \ - rm -rf /var/lib/apt/lists/* -RUN /usr/sbin/update-ccache-symlinks -RUN mkdir /opt/ccache && ccache --set-config=cache_dir=/opt/ccache -ENV PATH /opt/conda/bin:$PATH + unzip \ + bzip2 \ + ffmpeg \ + libsm6 \ + libxext6 \ + libpng-dev \ + python3 \ + python3-dev \ + python3-pip \ + python3-venv \ + && rm -rf /var/lib/apt/lists/* + +# Install uv for fast Python package management +COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv + +# ============================================================================= +# Stage 2: Python dependencies +# ============================================================================= +FROM base AS deps -# Instal environment -FROM dev-base as conda-installs -ARG PYTHON_VERSION=3.9 -ARG CUDA_VERSION=11.3 -ARG PYTORCH_VERSION=1.12.1 -ARG CUDA_CHANNEL=nvidia -ARG INSTALL_CHANNEL=pytorch -ENV CONDA_OVERRIDE_CUDA=${CUDA_VERSION} -RUN curl -fsSL -v -o ~/mambaforge.sh -O https://github.com/conda-forge/miniforge/releases/latest/download/Mambaforge-Linux-x86_64.sh && \ - chmod +x ~/mambaforge.sh && \ - ~/mambaforge.sh -b -p /opt/mamba && \ - rm ~/mambaforge.sh && \ - /opt/mamba/bin/mamba install -c "${INSTALL_CHANNEL}" -c "${CUDA_CHANNEL}" -y \ - python=${PYTHON_VERSION} \ - pytorch=${PYTORCH_VERSION} torchvision "cudatoolkit=${CUDA_VERSION}" && \ - /opt/mamba/bin/mamba clean -ya +WORKDIR /workspace +COPY pyproject.toml setup.py ./ +COPY theseus/__init__.py theseus/__init__.py -ENV PATH /opt/mamba/bin:$PATH -ENV NVIDIA_VISIBLE_DEVICES all -ENV NVIDIA_DRIVER_CAPABILITIES compute,utility -ENV LD_LIBRARY_PATH /usr/local/nvidia/lib:/usr/local/nvidia/lib64 -ENV PYTORCH_VERSION ${PYTORCH_VERSION} +# Install PyTorch with CUDA 12.4 support +RUN uv pip install --system \ + torch torchvision torchaudio \ + --index-url https://download.pytorch.org/whl/cu124 -# Install dependencies +# Install theseus with all optional dependencies COPY ./ /workspace/ -WORKDIR /workspace/ -RUN /opt/mamba/bin/python -m pip install --upgrade pip && \ - /opt/mamba/bin/python -m pip install -e .[cv,cv_classification,cv_semantic,cv_detection,nlp,nlp_retrieval,ml,dev] && \ - /opt/mamba/bin/python -m pip install dvc dvc-gdrive && \ - /opt/mamba/bin/python -m pip install -U timm +RUN uv pip install --system -e ".[all,dev]" + +# Install DVC for data versioning +RUN uv pip install --system dvc dvc-gdrive + +# ============================================================================= +# Stage 3: Data + Runtime +# ============================================================================= +FROM deps AS runtime + +WORKDIR /workspace -# Pull data from GDrive +# Pull data from GDrive (requires credentials secret) RUN --mount=type=secret,id=credentials \ - CREDENTIALS=$(cat /run/secrets/credentials) \ - && echo "$CREDENTIALS" > /workspace/credentials.json + CREDENTIALS=$(cat /run/secrets/credentials) \ + && echo "$CREDENTIALS" > /workspace/credentials.json RUN dvc remote modify gdrive --local gdrive_user_credentials_file /workspace/credentials.json RUN dvc pull diff --git a/__init__.py b/__init__.py new file mode 100644 index 00000000..877d31c0 --- /dev/null +++ b/__init__.py @@ -0,0 +1 @@ +from .theseus import * diff --git a/pyproject.toml b/pyproject.toml index fdf241a4..43cf3f20 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,20 +1,22 @@ [build-system] -# These are the assumed default build requirements from pip: -# https://pip.pypa.io/en/stable/reference/pip/#pep-517-and-518-support -requires = ["setuptools>=43.0.0", "wheel", "setuptools-scm"] +requires = ["setuptools>=68.0.0", "wheel", "setuptools-scm"] build-backend = "setuptools.build_meta" [tool.setuptools] packages = ["theseus"] +[[tool.uv.index]] +name = "pytorch" +url = "https://download.pytorch.org/whl/cu126" + [project] name = "theseus" -version = "1.5.0" -description = "A general template for various Deep Learning tasks. Strongly relies on Pytorch" +version = "2.0.0" +description = "A modular PyTorch Lightning framework for deep learning research and production training" readme = "README.md" -requires-python = ">=3.10" +requires-python = ">=3.10,<3.13" license = {file = "LICENSE"} -keywords = ["pytorch", "template", "deep learning"] +keywords = ["pytorch", "lightning", "template", "deep learning", "machine learning"] authors = [ {name = "Minh-Khoi Pham", email = "minhkhoi.pham@adaptcentre.ie" }, {name = "Hoang-Lan Nguyen", email = "nhlan091000@gmail.com"} @@ -25,86 +27,96 @@ maintainers = [ classifiers = [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", - "Topic :: Deep Learning :: Template", - "License :: MIT License", + "Intended Audience :: Science/Research", + "Topic :: Scientific/Engineering :: Artificial Intelligence", + "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", ] dependencies = [ - "seaborn>=0.12.0", - "plotly>=5.10.0", - "matplotlib>=3.4.3", - "webcolors>=1.12", - "wandb>=0.13.3", - "tensorboard>=2.10.0", - "pyyaml>=6.0", - "tqdm>=4.64.1", - "gdown>=4.5.1", - "tabulate>=0.8.10", - "loguru>=0.6.0", + "seaborn>=0.13.0", + "plotly>=5.18.0", + "matplotlib>=3.8.0", + "webcolors>=1.13", + "wandb>=0.17.0", + "pyyaml>=6.0.1", + "tqdm>=4.66.0", + "gdown>=5.1.0", + "tabulate>=0.9.0", + "loguru>=0.7.0", "kaleido>=0.2.1", - "optuna>=3.0.5", - "deepdiff>=6.2.3", + "deepdiff>=7.0.0", "hydra-core>=1.3.2", - "lightning>=2.0.0" + "scikit-learn>=1.7.2", + "omegaconf>=2.3.0", + "rich>=14.3.3", + "lightning[extra]>=2.4.0", + "scipy>=1.11.4", ] [project.optional-dependencies] -dev=[ - "pytest>=7.2.0", - "pytest-order>=1.0.1", - "pre-commit>=2.21.0", +dev = [ + "pytest>=8.0.0", + "pytest-order>=1.2.0", + "pre-commit>=3.6.0", + "ruff>=0.5.0", ] cv = [ - "albumentations>=1.1.0", - "omegaconf>=2.2.3" + "albumentations>=1.4.0,<2.0.0", + "omegaconf>=2.3.0", ] cv_classification = [ - "timm", + "timm>=1.0.0", "scikit-plot", - "grad-cam>=1.4.5" + "grad-cam>=1.5.0", ] cv_semantic = [ - "segmentation-models-pytorch>=0.3.0" + "segmentation-models-pytorch>=0.3.3", ] cv_detection = [ "ensemble_boxes>=1.0.9", - "pycocotools>=2.0.6", + "pycocotools>=2.0.7", ] nlp = [ - "pyvi>=0.1.1", - "py_vncorenlp>=0.1.3", - "spacy>=3.4.2", - "transformers>=4.25.1", - "nltk>=3.7", - "captum>=0.4.1" + "pyvi>=0.1.1", + "py_vncorenlp>=0.1.3", + "spacy>=3.7.0", + "transformers>=4.40.0", + "nltk>=3.8.0", + "captum>=0.7.0", ] nlp_retrieval = [ - "sentence_transformers>=2.2.2", - "rank_bm25>=0.2.2", - "elasticsearch>=7.17.7" + "sentence_transformers>=3.0.0", + "rank_bm25>=0.2.2", + "elasticsearch>=8.12.0", ] ml = [ - "pandas", - "pandarallel>=1.6.3", - "numpy", - "scikit-learn>=1.0.0", - "scipy>=1.7.0", - "optuna>=3.0.5", - "psycopg2-binary>=2.9.5", - "gunicorn>=20.1.0", - "lightgbm>=3.3.3", - "xgboost<=1.7.1", - "catboost", - "shap>=0.41.0", - "lime>=0.2.0.1", - "scikit-plot", + "optuna>=3.6.0", + "optuna-integration[wandb]>=4.8.0", + "numpy==1.26.4", + "lightgbm==4.6.0", + "xgboost>=3.0", + "shap==0.47.1", + "catboost==1.2.8", ] all = [ - "theseus[cv,cv_classification,cv_semantic,cv_detection,nlp,nlp_retrieval,ml]", + "theseus[cv,cv_classification,cv_semantic,cv_detection,nlp,nlp_retrieval,ml]", ] -[project.urls] # Optional +[project.urls] "Homepage" = "https://github.com/kaylode/theseus" +"Repository" = "https://github.com/kaylode/theseus" +"Issues" = "https://github.com/kaylode/theseus/issues" + +[tool.ruff] +target-version = "py310" +line-length = 100 + +[tool.ruff.lint] +select = ["E", "F", "W", "I", "UP", "B", "SIM"] +ignore = ["E501", "B905", "SIM108"] -[tool.isort] -profile = "black" +[tool.ruff.lint.isort] +known-first-party = ["theseus"] diff --git a/tests/classification/__init__.py b/tests/classification/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/classification/conftest.py b/tests/classification/conftest.py index e3f57b12..1a9b2f06 100644 --- a/tests/classification/conftest.py +++ b/tests/classification/conftest.py @@ -2,8 +2,7 @@ import optuna import pytest -from hydra import compose, initialize, initialize_config_module -from omegaconf import OmegaConf +from hydra import compose, initialize from optuna.storages import JournalFileStorage, JournalStorage from theseus.base.utilities.optuna_tuner import OptunaWrapper @@ -72,9 +71,7 @@ def override_tuner_config(): def override_tuner_tuner(): os.makedirs("runs/optuna/clf", exist_ok=True) - database = JournalStorage( - JournalFileStorage("runs/optuna/clf/pytest_clf_optuna.log") - ) + database = JournalStorage(JournalFileStorage("runs/optuna/clf/pytest_clf_optuna.log")) tuner = OptunaWrapper( storage=database, diff --git a/tests/classification/inference.py b/tests/classification/inference.py index 220d294a..68a8cf1e 100644 --- a/tests/classification/inference.py +++ b/tests/classification/inference.py @@ -17,7 +17,7 @@ class TestPipeline(BaseTestPipeline): def __init__(self, opt: DictConfig): - super(TestPipeline, self).__init__(opt) + super().__init__(opt) self.opt = opt def init_globals(self): @@ -36,13 +36,13 @@ def inference(self): df_dict = {"filename": [], "label": [], "score": []} - for idx, batch in enumerate(tqdm(self.dataloader)): + for _idx, batch in enumerate(tqdm(self.dataloader)): img_names = batch["img_names"] outputs = self.model.predict_step(batch) preds = outputs["names"] probs = outputs["confidences"] - for (filename, pred, prob) in zip(img_names, preds, probs): + for filename, pred, prob in zip(img_names, preds, probs): df_dict["filename"].append(filename) df_dict["label"].append(pred) df_dict["score"].append(prob) diff --git a/tests/classification/test_tuner_clf.py b/tests/classification/test_tuner_clf.py index 0f9d6fab..a7f87037 100644 --- a/tests/classification/test_tuner_clf.py +++ b/tests/classification/test_tuner_clf.py @@ -21,9 +21,7 @@ def test_train_clf_tune(override_tuner_config, override_tuner_tuner): leaderboard_df = override_tuner_tuner.leaderboard() os.makedirs("runs/optuna/clf/overview", exist_ok=True) # leaderboard_df.to_csv("runs/optuna/clf/overview/leaderboard.csv", index=False) - leaderboard_df.to_json( - "runs/optuna/clf/overview/leaderboard.json", orient="records" - ) + leaderboard_df.to_json("runs/optuna/clf/overview/leaderboard.json", orient="records") # figs = override_tuner_tuner.visualize("all") # for fig_type, fig in figs: diff --git a/tests/semantic/__init__.py b/tests/semantic/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/semantic/inference.py b/tests/semantic/inference.py index 394d9624..09cf2143 100644 --- a/tests/semantic/inference.py +++ b/tests/semantic/inference.py @@ -18,7 +18,7 @@ class TestPipeline(BaseTestPipeline): def __init__(self, opt: DictConfig): - super(TestPipeline, self).__init__(opt) + super().__init__(opt) self.opt = opt def init_globals(self): @@ -44,7 +44,7 @@ def inference(self): os.makedirs(saved_mask_dir, exist_ok=True) os.makedirs(saved_overlay_dir, exist_ok=True) - for idx, batch in enumerate(self.dataloader): + for _idx, batch in enumerate(self.dataloader): inputs = batch["inputs"] img_names = batch["img_names"] ori_sizes = batch["ori_sizes"] @@ -52,9 +52,7 @@ def inference(self): outputs = self.model.predict_step(batch) preds = outputs["masks"] - for (inpt, pred, filename, ori_size) in zip( - inputs, preds, img_names, ori_sizes - ): + for inpt, pred, filename, ori_size in zip(inputs, preds, img_names, ori_sizes): decode_pred = visualizer.decode_segmap(pred)[:, :, ::-1] resized_decode_mask = cv2.resize(decode_pred, tuple(ori_size)) diff --git a/tests/semantic/test_segm.py b/tests/semantic/test_segm.py index d9b0bd7d..cc04a632 100644 --- a/tests/semantic/test_segm.py +++ b/tests/semantic/test_segm.py @@ -19,8 +19,6 @@ def test_eval_clf(override_config): @pytest.mark.order(2) def test_infer_clf(override_test_config): - override_test_config["global"][ - "pretrained" - ] = "runs/pytest_segm/checkpoints/best.ckpt" + override_test_config["global"]["pretrained"] = "runs/pytest_segm/checkpoints/best.ckpt" test_pipeline = TestPipeline(override_test_config) test_pipeline.inference() diff --git a/tests/tabular/__init__.py b/tests/tabular/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/tabular/conftest.py b/tests/tabular/conftest.py index 7fccd21b..5ba887e6 100644 --- a/tests/tabular/conftest.py +++ b/tests/tabular/conftest.py @@ -1,58 +1,48 @@ -import os - -import pytest -from hydra import compose, initialize -from optuna.storages import JournalFileStorage, JournalStorage - -from theseus.base.utilities.optuna_tuner import OptunaWrapper - -MODELS = ["xgboost"] # , "catboost", 'lightgbm'] -TUNER_MODELS = ["xgboost_tune"] # , "catboost_tune"] #, 'lightgbm_tune'] - - -@pytest.fixture(scope="session", params=MODELS) -def override_config(request): - with initialize(config_path="configs"): - config = compose( - config_name=f"{request.param}", - overrides=[ - "global.exp_name=pytest_tablr", - "global.exist_ok=True", - "global.save_dir=runs", - ], - ) - - return config - - -@pytest.fixture(scope="function", params=TUNER_MODELS) -def override_tuner_config(request): - with initialize(config_path="configs/optuna"): - config = compose( - config_name=f"{request.param}", - overrides=[ - "global.exp_name=pytest_tablr_optuna", - "global.exist_ok=True", - "global.save_dir=runs", - ], - ) - - return config - - -@pytest.fixture(scope="session") -def override_tuner_tuner(): - os.makedirs("runs/optuna/tablr", exist_ok=True) - database = JournalStorage( - JournalFileStorage("runs/optuna/tablr/pytest_tablr_optuna.log") - ) - - tuner = OptunaWrapper( - storage=database, - study_name="pytest_tablr_optuna", - n_trials=3, - direction="maximize", - save_dir="runs/optuna/tablr", - ) - - return tuner +import pandas as pd +import pytest + +from theseus.ml.preprocessors import FillNaN, LabelEncode, PreprocessCompose, Standardize + + +# --- Data Loading Helper --- +def load_titanic_data(split: str): + """Load and preprocess Titanic data for a given split.""" + data_path = f"samples/titanic/{split}.csv" + classnames_path = "samples/titanic/classnames.txt" + target_column = "Survived" + + df = pd.read_csv(data_path) + + # Apply preprocessing (same pipeline as the old YAML config) + transform = PreprocessCompose( + preproc_list=[ + FillNaN(column_names=["Embarked", "Cabin"], fill_with="None"), + FillNaN(column_names=["Age"], fill_with=0), + LabelEncode(), + Standardize(method="minmax", column_names=["*"], exclude_columns=[target_column]), + ] + ) + df = transform.run(df) + + X = df.drop(target_column, axis=1).values + y = df[target_column].values + feature_names = list(df.drop(target_column, axis=1).columns) + classnames = open(classnames_path).read().splitlines() + + return X, y, feature_names, classnames + + +# --- Fixtures --- +@pytest.fixture(scope="session") +def titanic_data(): + """Load train and val splits of Titanic dataset.""" + X_train, y_train, feature_names, classnames = load_titanic_data("train") + X_val, y_val, _, _ = load_titanic_data("val") + return { + "X_train": X_train, + "X_val": X_val, + "y_train": y_train, + "y_val": y_val, + "feature_names": feature_names, + "classnames": classnames, + } diff --git a/tests/tabular/test_tablr.py b/tests/tabular/test_tablr.py index 56377883..3b2d9a3d 100644 --- a/tests/tabular/test_tablr.py +++ b/tests/tabular/test_tablr.py @@ -1,24 +1,64 @@ -import pytest - -# from configs.tabular.infer import TestPipeline -from theseus.ml.pipeline import MLPipeline - - -@pytest.mark.order(1) -def test_train_tblr(override_config): - train_pipeline = MLPipeline(override_config) - train_pipeline.fit() - - -@pytest.mark.order(2) -def test_eval_tblr(override_config): - override_config["global"]["pretrained"] = "runs/pytest_tablr/checkpoints/last" - val_pipeline = MLPipeline(override_config) - val_pipeline.evaluate() - - -# @pytest.mark.order(2) -# def test_infer_clf(override_test_config): -# override_test_config["global"]["weights"] = "runs/pytest_segm/checkpoints/best.pth" -# test_pipeline = TestPipeline(override_test_config) -# test_pipeline.inference() +import numpy as np +import pytest + +from theseus.ml.tradml import fit_xgboost + + +@pytest.mark.order(1) +def test_train_xgboost(titanic_data): + """Train an XGBoost classifier on Titanic data.""" + model, params = fit_xgboost( + params={ + "n_estimators": 50, + "max_depth": 5, + "learning_rate": 0.1, + "gamma": 0.1, + "reg_alpha": 0, + "reg_lambda": 1, + }, + X_train=titanic_data["X_train"], + X_val=titanic_data["X_val"], + y_train=titanic_data["y_train"], + y_val=titanic_data["y_val"], + is_classification=True, + ) + assert model is not None + assert "objective" in params + + # Evaluate + preds = model.predict_proba(titanic_data["X_val"]) + pred_labels = np.argmax(preds, axis=1) + from sklearn.metrics import accuracy_score + + acc = accuracy_score(titanic_data["y_val"], pred_labels) + print(f"XGBoost accuracy: {acc:.4f}") + assert acc > 0.5, f"Expected accuracy > 0.5, got {acc}" + + +@pytest.mark.order(2) +def test_eval_xgboost(titanic_data): + """Train and evaluate XGBoost — verify metrics are reasonable.""" + model, _ = fit_xgboost( + params={ + "n_estimators": 50, + "max_depth": 5, + "learning_rate": 0.1, + "gamma": 0.1, + "reg_alpha": 0, + "reg_lambda": 1, + }, + X_train=titanic_data["X_train"], + X_val=titanic_data["X_val"], + y_train=titanic_data["y_train"], + y_val=titanic_data["y_val"], + is_classification=True, + ) + + from sklearn.metrics import f1_score, matthews_corrcoef + + preds = model.predict_proba(titanic_data["X_val"]) + pred_labels = np.argmax(preds, axis=1) + f1 = f1_score(titanic_data["y_val"], pred_labels, average="macro") + mcc = matthews_corrcoef(titanic_data["y_val"], pred_labels) + print(f"F1: {f1:.4f}, MCC: {mcc:.4f}") + assert f1 > 0.0 diff --git a/tests/tabular/test_tuner_tblr.py b/tests/tabular/test_tuner_tblr.py index c5e094ba..fddbd12c 100644 --- a/tests/tabular/test_tuner_tblr.py +++ b/tests/tabular/test_tuner_tblr.py @@ -1,29 +1,43 @@ -import os - -import pytest - -from theseus.ml.callbacks.optuna_callbacks import OptunaCallbacks -from theseus.ml.pipeline import MLPipeline - - -@pytest.mark.order(1) -def test_train_tblr_tune(override_tuner_config, override_tuner_tuner): - override_tuner_tuner.tune( - config=override_tuner_config, - pipeline_class=MLPipeline, - optuna_callback=OptunaCallbacks, - trial_user_attrs={ - "best_key": "bl_acc", - "model_name": override_tuner_config["model"]["args"]["model_name"], - }, - ) - - leaderboard_df = override_tuner_tuner.leaderboard() - os.makedirs("runs/optuna/tablr/overview", exist_ok=True) - leaderboard_df.to_json( - "runs/optuna/tablr/overview/leaderboard.json", orient="records" - ) - - figs = override_tuner_tuner.visualize("all") - for fig_type, fig in figs: - fig.write_image(f"runs/optuna/tablr/overview/{fig_type}.png") +import os + +import pytest + +from theseus.ml.tradml import TradMLTuner + + +@pytest.mark.order(3) +def test_tuner_xgboost(titanic_data): + """Run TradMLTuner with XGBoost on Titanic data (2 trials, no wandb).""" + save_dir = "runs/optuna/tablr_test" + os.makedirs(save_dir, exist_ok=True) + storage_path = os.path.join(save_dir, "test_tuner.log") + + tuner = TradMLTuner( + storage=storage_path, + study_name="pytest_xgboost_tune", + n_trials=2, + direction="maximize", + save_dir=save_dir, + method="xgboost", + wandb_kwargs=None, + feature_names=titanic_data["feature_names"], + classnames=titanic_data["classnames"], + ) + + best_model = tuner.tune( + X_train=titanic_data["X_train"], + X_val=titanic_data["X_val"], + y_train=titanic_data["y_train"], + y_val=titanic_data["y_val"], + is_classification=True, + ) + + assert best_model is not None + + # Check leaderboard + df = tuner.leaderboard() + assert len(df) >= 2 + print(f"Tuner leaderboard:\n{df}") + + # Check best config was saved + assert os.path.exists(os.path.join(save_dir, "best_config.json")) diff --git a/theseus/__init__.py b/theseus/__init__.py index 6f39759a..0c77b113 100644 --- a/theseus/__init__.py +++ b/theseus/__init__.py @@ -1,5 +1,5 @@ """ -Theseus - Templates and useful tools for Deep Learning +Theseus - A modular PyTorch Lightning framework for deep learning ~~~~~~~~~~~~~~~~~~~ :copyright: (c) 2020-present Kaylode @@ -11,7 +11,26 @@ __author__ = "kaylode" __license__ = "MIT" __copyright__ = "Copyright 2020-present Kaylode" -__version__ = "1.5.0" +__version__ = "2.0.0" -from .base import * +from .base.utilities import ( + LoggerObserver, + download_from_wandb, + find_file_recursively, + get_devices_info, + get_instance_recursively, + move_to, + seed_everything, +) from .registry import Registry + +__all__ = [ + "move_to", + "seed_everything", + "LoggerObserver", + "find_file_recursively", + "get_devices_info", + "download_from_wandb", + "get_instance_recursively", + "Registry", +] diff --git a/theseus/base/__init__.py b/theseus/base/__init__.py index 42cc6817..39ab387a 100644 --- a/theseus/base/__init__.py +++ b/theseus/base/__init__.py @@ -1,9 +1,3 @@ -from .augmentations import * -from .callbacks import * -from .datasets import * -from .losses import * -from .metrics import * -from .models import * -from .optimizers import * -from .pipeline import * -from .trainer import * +# Expose nothing by default to prevent heavy dependency imports (like torch/lightning). +# Users should explicitly import what they need, e.g., +# `from theseus.base.models import LightningModelWrapper` diff --git a/theseus/base/callbacks/__init__.py b/theseus/base/callbacks/__init__.py index a9b0c117..e384cc7b 100644 --- a/theseus/base/callbacks/__init__.py +++ b/theseus/base/callbacks/__init__.py @@ -8,6 +8,7 @@ from theseus.registry import Registry from .checkpoint_callback import TorchCheckpointCallback +from .hub_callback import HuggingFaceHubCallback from .loss_logging_callback import LossLoggerCallback from .metric_logging_callback import MetricLoggerCallback from .timer_callback import TimerCallback @@ -26,3 +27,4 @@ CALLBACKS_REGISTRY.register(LossLoggerCallback) CALLBACKS_REGISTRY.register(MetricLoggerCallback) CALLBACKS_REGISTRY.register(TorchCheckpointCallback) +CALLBACKS_REGISTRY.register(HuggingFaceHubCallback) diff --git a/theseus/base/callbacks/checkpoint_callback.py b/theseus/base/callbacks/checkpoint_callback.py index e544a533..bae88ccc 100644 --- a/theseus/base/callbacks/checkpoint_callback.py +++ b/theseus/base/callbacks/checkpoint_callback.py @@ -21,14 +21,11 @@ def __init__(self, save_dir: str, **kwargs) -> None: super().__init__(dirpath=save_dir, **filtered_kwargs) - def setup( - self, trainer: pl.Trainer, pl_module: pl.LightningModule, stage: str - ) -> None: + def setup(self, trainer: pl.Trainer, pl_module: pl.LightningModule, stage: str) -> None: super().setup(trainer, pl_module, stage) self.params = {} trainloader = pl_module.datamodule.trainloader if trainloader is not None: - batch_size = trainloader.batch_size self.params["trainloader_length"] = len(trainloader) self.params["num_iterations"] = len(trainloader) * trainer.max_epochs @@ -49,7 +46,7 @@ def auto_get_save_interval(self, train_fraction=0.5): def _save_checkpoint(self, trainer: pl.Trainer, filepath: str) -> None: super()._save_checkpoint(trainer, filepath) - if filepath in self.best_k_models.keys(): + if filepath in self.best_k_models: if self.best_k_models[filepath] == self.best_model_score: LOGGER.text( f"Evaluation improved to {self.current_score}", diff --git a/theseus/base/callbacks/hub_callback.py b/theseus/base/callbacks/hub_callback.py new file mode 100644 index 00000000..dc034e33 --- /dev/null +++ b/theseus/base/callbacks/hub_callback.py @@ -0,0 +1,93 @@ +""" +Lightning callback for pushing model checkpoints to HuggingFace Hub. +""" + +from __future__ import annotations + +import logging +from typing import Any + +import lightning.pytorch as pl +from lightning.pytorch.callbacks import Callback + +logger = logging.getLogger(__name__) + + +class HuggingFaceHubCallback(Callback): + """ + Lightning callback that pushes model checkpoints to HuggingFace Hub. + + Can be configured to push on best metric, periodically, or at training end. + + Example config:: + + callbacks: + - name: HuggingFaceHubCallback + args: + repo_id: "username/my-model" + push_on_train_end: true + push_every_n_epochs: 5 + + Args: + repo_id: HuggingFace repo ID (e.g. ``username/model-name``). + token: HuggingFace API token. + push_on_train_end: Push at end of training. + push_every_n_epochs: Push every N epochs (0 = disabled). + private: Whether the repo should be private. + use_safetensors: Use safetensors format. + config: Optional config dict to include. + """ + + def __init__( + self, + repo_id: str, + *, + token: str | None = None, + push_on_train_end: bool = True, + push_every_n_epochs: int = 0, + private: bool = False, + use_safetensors: bool = True, + config: dict | None = None, + **kwargs: Any, + ) -> None: + super().__init__() + self.repo_id = repo_id + self.token = token + self.push_on_train_end = push_on_train_end + self.push_every_n_epochs = push_every_n_epochs + self.private = private + self.use_safetensors = use_safetensors + self.config = config or {} + + def _push_model(self, trainer: pl.Trainer, pl_module: pl.LightningModule) -> None: + """Push the underlying model to HuggingFace Hub.""" + from theseus.base.utilities.hub import HuggingFaceHubMixin + + model = getattr(pl_module, "model", pl_module) + + if isinstance(model, HuggingFaceHubMixin): + try: + model.push_to_hub( + self.repo_id, + token=self.token, + private=self.private, + config=self.config, + use_safetensors=self.use_safetensors, + commit_message=f"Epoch {trainer.current_epoch}", + ) + except Exception as e: + logger.warning(f"Failed to push model to HuggingFace Hub: {e}") + else: + logger.warning( + f"Model {type(model).__name__} does not inherit from " + f"HuggingFaceHubMixin. Skipping push." + ) + + def on_train_epoch_end(self, trainer: pl.Trainer, pl_module: pl.LightningModule) -> None: + if self.push_every_n_epochs > 0: + if (trainer.current_epoch + 1) % self.push_every_n_epochs == 0: + self._push_model(trainer, pl_module) + + def on_train_end(self, trainer: pl.Trainer, pl_module: pl.LightningModule) -> None: + if self.push_on_train_end: + self._push_model(trainer, pl_module) diff --git a/theseus/base/callbacks/loss_logging_callback.py b/theseus/base/callbacks/loss_logging_callback.py index f23adfb2..3f745631 100644 --- a/theseus/base/callbacks/loss_logging_callback.py +++ b/theseus/base/callbacks/loss_logging_callback.py @@ -1,5 +1,5 @@ import time -from typing import Any, Dict, List +from typing import Any import lightning.pytorch as pl import numpy as np @@ -28,9 +28,7 @@ def __init__(self, print_interval: int = None, **kwargs) -> None: self.running_loss = {} self.print_interval = print_interval - def setup( - self, trainer: pl.Trainer, pl_module: pl.LightningModule, stage: str - ) -> None: + def setup(self, trainer: pl.Trainer, pl_module: pl.LightningModule, stage: str) -> None: """ Setup the callback """ @@ -38,7 +36,6 @@ def setup( trainloader = pl_module.datamodule.trainloader if trainloader is not None: - batch_size = trainloader.batch_size self.params["num_iterations"] = len(trainloader) * trainer.max_epochs self.params["trainloader_length"] = len(trainloader) else: @@ -47,14 +44,12 @@ def setup( valloader = pl_module.datamodule.valloader if valloader is not None: - batch_size = valloader.batch_size self.params["valloader_length"] = len(valloader) else: self.params["valloader_length"] = None testloader = pl_module.datamodule.testloader if testloader is not None: - batch_size = testloader.batch_size self.params["testloader_length"] = len(testloader) else: self.params["testloader_length"] = None @@ -62,13 +57,11 @@ def setup( if self.print_interval is None: self.print_interval = self.auto_get_print_interval(pl_module) LOGGER.text( - "Print interval not specified. Auto calculating...", + f"Print interval not specified. Auto calculated and set to {self.print_interval} iterations.", level=LoggerObserver.DEBUG, ) - def auto_get_print_interval( - self, pl_module: pl.LightningModule, train_fraction: float = 0.1 - ): + def auto_get_print_interval(self, pl_module: pl.LightningModule, train_fraction: float = 0.1): """ Automatically decide the number of print interval """ @@ -81,6 +74,7 @@ def auto_get_print_interval( else self.params["testloader_length"] ) print_interval = max(int(train_fraction * num_iterations_per_epoch), 1) + return print_interval def on_train_epoch_start(self, trainer: pl.Trainer, pl_module: pl.LightningModule): @@ -119,29 +113,25 @@ def on_train_batch_end( loss_dict = outputs["loss_dict"] # Update running loss of batch - for (key, value) in loss_dict.items(): - if key not in self.running_loss.keys(): + for key, value in loss_dict.items(): + if key not in self.running_loss: self.running_loss[key] = [] self.running_loss[key].append(value) # Running time since last interval batch_time = time.time() - self.running_time + if getattr(self, "running_time_list", None) is None: + self.running_time_list = [] self.running_time_list.append(batch_time) # Logging - if ( - iters % self.print_interval == 0 - or (iters + 1) % self.params["trainloader_length"] == 0 - ): - + if (iters % self.print_interval == 0 and iters > 0) or (iters + 1) % self.params[ + "trainloader_length" + ] == 0: # Running loss since last interval - for key in self.running_loss.keys(): + for key in self.running_loss: self.running_loss[key] = np.round(np.mean(self.running_loss[key]), 5) - loss_string = ( - "{}".format(self.running_loss)[1:-1] - .replace("'", "") - .replace(",", " ||") - ) + loss_string = f"{self.running_loss}"[1:-1].replace("'", "").replace(",", " ||") # Running time average running_time = 1.0 / np.round(np.mean(self.running_time_list), 5) @@ -169,7 +159,7 @@ def on_train_batch_end( # Log batch time execution log_dict.append( { - "tag": f"Training/Iterations per second", + "tag": "Training/Iterations per second", "value": running_time, "type": LoggerObserver.SCALAR, "kwargs": {"step": iters}, @@ -192,9 +182,7 @@ def on_train_batch_end( self.running_loss = {} self.running_time_list = [] - def on_validation_epoch_start( - self, trainer: pl.Trainer, pl_module: pl.LightningModule - ): + def on_validation_epoch_start(self, trainer: pl.Trainer, pl_module: pl.LightningModule): """ Before main validation loops """ @@ -217,8 +205,8 @@ def on_validation_batch_end( loss_dict = outputs["loss_dict"] # Update batch loss - for (key, value) in loss_dict.items(): - if key not in self.running_loss.keys(): + for key, value in loss_dict.items(): + if key not in self.running_loss: self.running_loss[key] = [] self.running_loss[key].append(value) @@ -232,11 +220,9 @@ def on_validation_end(self, trainer: pl.Trainer, pl_module: pl.LightningModule): epoch_time = time.time() - self.running_time # Log loss - for key in self.running_loss.keys(): + for key in self.running_loss: self.running_loss[key] = np.round(np.mean(self.running_loss[key]), 5) - loss_string = ( - "{}".format(self.running_loss)[1:-1].replace("'", "").replace(",", " ||") - ) + loss_string = f"{self.running_loss}"[1:-1].replace("'", "").replace(",", " ||") LOGGER.text( "[{}|{}] || {} || Time: {:10.4f} (it/s)".format( iters, @@ -283,11 +269,9 @@ def on_test_end(self, trainer: pl.Trainer, pl_module: pl.LightningModule): epoch_time = time.time() - self.running_time # Log loss - for key in self.running_loss.keys(): + for key in self.running_loss: self.running_loss[key] = np.round(np.mean(self.running_loss[key]), 5) - loss_string = ( - "{}".format(self.running_loss)[1:-1].replace("'", "").replace(",", " ||") - ) + loss_string = f"{self.running_loss}"[1:-1].replace("'", "").replace(",", " ||") LOGGER.text( "[{}|{}] || {} || Time: {:10.4f} (it/s)".format( iters, diff --git a/theseus/base/callbacks/metric_logging_callback.py b/theseus/base/callbacks/metric_logging_callback.py index 3fdcc793..b4b711e7 100644 --- a/theseus/base/callbacks/metric_logging_callback.py +++ b/theseus/base/callbacks/metric_logging_callback.py @@ -1,7 +1,6 @@ import json import os import os.path as osp -from typing import Dict import lightning.pytorch as pl from lightning.pytorch.callbacks import Callback @@ -21,12 +20,10 @@ class MetricLoggerCallback(Callback): def __init__(self, save_json: bool = True, **kwargs) -> None: super().__init__() self.save_json = save_json - self.save_dir = kwargs.get("save_dir", None) + self.save_dir = kwargs.get("save_dir") self.output_dict = [] - def on_validation_end( - self, trainer: pl.Trainer, pl_module: pl.LightningModule - ) -> None: + def on_validation_end(self, trainer: pl.Trainer, pl_module: pl.LightningModule) -> None: """ After finish validation """ @@ -94,23 +91,20 @@ def on_test_end(self, trainer: pl.Trainer, pl_module: pl.LightningModule) -> Non LOGGER.log(log_dict) - def teardown( - self, trainer: pl.Trainer, pl_module: pl.LightningModule, stage: str - ) -> None: + def teardown(self, trainer: pl.Trainer, pl_module: pl.LightningModule, stage: str) -> None: """ After finish everything """ - if self.save_json: - if self.save_dir is not None: - save_dir = osp.join(self.save_dir, stage.capitalize()) - os.makedirs(save_dir, exist_ok=True) - save_json = osp.join(save_dir, "metrics.json") - if len(self.output_dict) > 0: - with open(save_json, "w") as f: - json.dump( - self.output_dict, - f, - indent=4, - default=lambda x: "", - ) + if self.save_json and self.save_dir is not None: + save_dir = osp.join(self.save_dir, stage.capitalize()) + os.makedirs(save_dir, exist_ok=True) + save_json = osp.join(save_dir, "metrics.json") + if len(self.output_dict) > 0: + with open(save_json, "w") as f: + json.dump( + self.output_dict, + f, + indent=4, + default=lambda x: "", + ) diff --git a/theseus/base/callbacks/optuna_callback.py b/theseus/base/callbacks/optuna_callback.py index f93c7146..0b71a7c5 100644 --- a/theseus/base/callbacks/optuna_callback.py +++ b/theseus/base/callbacks/optuna_callback.py @@ -1,4 +1,3 @@ -from typing import Dict, List import lightning.pytorch as pl import optuna @@ -30,7 +29,5 @@ def on_validation_end(self, trainer: pl.Trainer, pl_module: pl.LightningModule): self.trial.report(value=metric_dict[best_key], step=iters) if self.trial.should_prune(): - LOGGER.text( - f"Trial {self.trial.number} has been pruned", level=LoggerObserver.DEBUG - ) + LOGGER.text(f"Trial {self.trial.number} has been pruned", level=LoggerObserver.DEBUG) raise optuna.TrialPruned() diff --git a/theseus/base/callbacks/timer_callback.py b/theseus/base/callbacks/timer_callback.py index b07940e9..969fc185 100644 --- a/theseus/base/callbacks/timer_callback.py +++ b/theseus/base/callbacks/timer_callback.py @@ -33,7 +33,7 @@ def on_fit_start(self, trainer: pl.Trainer, pl_module: pl.LightningModule) -> No """ self.start_time = time.time() LOGGER.text( - f"===========================START TRAINING=================================", + "===========================START TRAINING=================================", level=LoggerObserver.INFO, ) @@ -60,6 +60,8 @@ def on_train_epoch_end(self, trainer: pl.Trainer, pl_module: pl.LightningModule) """ After going to the training loop """ + if getattr(trainer, "train_epoch_start_time", None) is None: + self.train_epoch_start_time = time.time() running_time = time.time() - self.train_epoch_start_time h, m, s = seconds_to_hours(running_time) LOGGER.text( @@ -82,6 +84,8 @@ def on_validation_end(self, trainer: pl.Trainer, pl_module: pl.LightningModule): After finish validation """ + if getattr(trainer, "val_epoch_start_time", None) is not None: + self.val_epoch_start_time = time.time() running_time = time.time() - self.val_epoch_start_time h, m, s = seconds_to_hours(running_time) LOGGER.text( diff --git a/theseus/base/callbacks/tsb_callback.py b/theseus/base/callbacks/tsb_callback.py index 49b07d16..88d8c293 100644 --- a/theseus/base/callbacks/tsb_callback.py +++ b/theseus/base/callbacks/tsb_callback.py @@ -33,7 +33,5 @@ def __init__(self, save_dir: str = "runs", resume: str = None, **kwargs) -> None """ tsb_logger = TensorboardLogger(self.save_dir) if self.resume is not None: - tsb_logger.load( - find_old_tflog(os.path.dirname(os.path.dirname(self.resume))) - ) + tsb_logger.load(find_old_tflog(os.path.dirname(os.path.dirname(self.resume)))) LOGGER.subscribe(tsb_logger) diff --git a/theseus/base/callbacks/wandb_callback.py b/theseus/base/callbacks/wandb_callback.py index 361c16a9..a233729c 100644 --- a/theseus/base/callbacks/wandb_callback.py +++ b/theseus/base/callbacks/wandb_callback.py @@ -1,42 +1,41 @@ +import contextlib import os import os.path as osp from copy import deepcopy from datetime import datetime -from typing import Dict import lightning.pytorch as pl from deepdiff import DeepDiff from lightning.pytorch.callbacks import Callback +from lightning.pytorch.utilities.model_summary import summarize from omegaconf import DictConfig, OmegaConf from theseus.base.utilities.loggers.observer import LoggerObserver from theseus.base.utilities.loggers.wandb_logger import WandbLogger, find_run_id -try: +with contextlib.suppress(ModuleNotFoundError): import wandb as wandblogger -except ModuleNotFoundError: - pass LOGGER = LoggerObserver.getLogger("main") def pretty_print_diff(diff): texts = [] - for type_key in diff.keys(): - for config_key in diff[type_key].keys(): - if type_key == "values_changed": - texts.append( - config_key - + ": " - + str(diff[type_key][config_key]["old_value"]) - + "-->" - + str(diff[type_key][config_key]["new_value"]) - ) - elif "item_removed" in type_key: - texts.append(config_key + ": " + str(diff[type_key][config_key])) - elif "item_added" in type_key: - texts.append(config_key + ": " + str(diff[type_key][config_key])) - + for type_key in diff: + try: + for config_key in diff[type_key]: + if type_key == "values_changed": + texts.append( + config_key + + ": " + + str(diff[type_key][config_key]["old_value"]) + + "-->" + + str(diff[type_key][config_key]["new_value"]) + ) + elif "item_removed" in type_key or "item_added" in type_key: + texts.append(config_key + ": " + str(diff[type_key][config_key])) + except: + texts.append(str(type_key) + ": " + str(diff[type_key])) return "\n".join(texts) @@ -62,6 +61,7 @@ def __init__( save_dir: str = None, resume: str = None, config_dict: DictConfig = None, + log_checkpoints: bool = True, **kwargs, ) -> None: super().__init__() @@ -71,6 +71,7 @@ def __init__( self.resume = resume self.save_dir = save_dir self.config_dict = config_dict + self.log_checkpoints = log_checkpoints # A hack, not good if self.save_dir is None: @@ -101,6 +102,7 @@ def __init__( # Check if the config remains the same, if not, create new run id old_config_dict = OmegaConf.load(old_config_path) tmp_config_dict = deepcopy(self.config_dict) + tmp_config_dict = DictConfig(tmp_config_dict) ## strip off global key because `resume` will always different old_config_dict.pop("global", None) OmegaConf.set_struct(tmp_config_dict, False) @@ -129,7 +131,7 @@ def __init__( ) answer = int(input()) - assert answer in [1, 2], "Wrong input" + assert answer in [1, 2, 3], "Wrong input" if answer == 2: LOGGER.text( "Creating new wandb run...", @@ -168,9 +170,7 @@ def __init__( ) LOGGER.subscribe(self.wandb_logger) - def setup( - self, trainer: pl.Trainer, pl_module: pl.LightningModule, stage: str - ) -> None: + def setup(self, trainer: pl.Trainer, pl_module: pl.LightningModule, stage: str) -> None: """ Before going to the main loop. Save run id """ @@ -179,30 +179,64 @@ def setup( f.write(self.id) # Save all config files - self.wandb_logger.log_file( - tag="configs", - base_folder=self.save_dir, - value=osp.join(self.save_dir, "*.yaml"), + if self.log_checkpoints: + self.wandb_logger.log_file( + tag="configs", + base_folder=self.save_dir, + value=osp.join(self.save_dir, "*.yaml"), + ) + + summary = summarize(pl_module, max_depth=3) + + LOGGER.text( + f"Model summary:\n{summary}", + level=LoggerObserver.INFO, ) + total_params = summary.total_parameters + total_trainable_params = summary.trainable_parameters + + # Log learning rates + log_dict = [ + { + "tag": "Training/Total no. parameters", + "value": total_params, + "type": LoggerObserver.SCALAR, + "kwargs": {"step": 0}, + } + ] + + log_dict.append( + { + "tag": "Training/Total no. trainable parameters", + "value": total_trainable_params, + "type": LoggerObserver.SCALAR, + "kwargs": {"step": 0}, + } + ) + + LOGGER.log(log_dict) + def teardown(self, trainer: pl.Trainer, pl_module: pl.LightningModule, stage: str): """ After finish training """ base_folder = osp.join(self.save_dir, "checkpoints") - self.wandb_logger.log_file( - tag="checkpoint", - base_folder=self.save_dir, - value=osp.join(base_folder, "*.ckpt"), - ) + if self.log_checkpoints: + self.wandb_logger.log_file( + tag="checkpoint", + base_folder=self.save_dir, + value=osp.join(base_folder, "*.ckpt"), + ) def on_validation_end(self, trainer: pl.Trainer, pl_module: pl.LightningModule): """ On validation batch (iteration) end """ base_folder = osp.join(self.save_dir, "checkpoints") - self.wandb_logger.log_file( - tag="checkpoint", - base_folder=self.save_dir, - value=osp.join(base_folder, "*.ckpt"), - ) + if self.log_checkpoints: + self.wandb_logger.log_file( + tag="checkpoint", + base_folder=self.save_dir, + value=osp.join(base_folder, "*.ckpt"), + ) diff --git a/theseus/base/datasets/collator.py b/theseus/base/datasets/collator.py index aeee091b..25fa5dcc 100644 --- a/theseus/base/datasets/collator.py +++ b/theseus/base/datasets/collator.py @@ -1,7 +1,6 @@ -from typing import List -class BaseCollator(object): +class BaseCollator: """Base collator function""" def __init__(self, **kwargs) -> None: @@ -14,7 +13,7 @@ def __call__(self, batch): class ChainCollatorWrapper(BaseCollator): """Wrapper for list of collate functions""" - def __init__(self, pre_collate_fns: List, **kwargs): + def __init__(self, pre_collate_fns: list, **kwargs): self.pre_collate_fns = pre_collate_fns def __call__(self, batch): diff --git a/theseus/base/datasets/dataloader.py b/theseus/base/datasets/dataloader.py index 91e075a4..53bc581c 100644 --- a/theseus/base/datasets/dataloader.py +++ b/theseus/base/datasets/dataloader.py @@ -16,6 +16,4 @@ def __init__(self, dataset, collate_fn=None, sampler=None, **kwargs) -> None: else: collate_fn = dataset.collate_fn - super().__init__( - dataset=dataset, collate_fn=collate_fn, sampler=sampler, **kwargs - ) + super().__init__(dataset=dataset, collate_fn=collate_fn, sampler=sampler, **kwargs) diff --git a/theseus/base/datasets/dataset.py b/theseus/base/datasets/dataset.py index 2df139aa..ea7038dd 100644 --- a/theseus/base/datasets/dataset.py +++ b/theseus/base/datasets/dataset.py @@ -1,5 +1,5 @@ import os -from typing import Iterable, List +from collections.abc import Iterable import numpy as np import torch @@ -61,11 +61,7 @@ class ImageDataset(data.Dataset): """ def __init__( - self, - image_dir: str, - txt_classnames: str = None, - transform: List = None, - **kwargs + self, image_dir: str, txt_classnames: str = None, transform: list = None, **kwargs ): super().__init__() self.image_dir = image_dir @@ -78,7 +74,7 @@ def load_data(self): Load filepaths into memory """ if self.txt_classnames: - with open(self.txt_classnames, "r") as f: + with open(self.txt_classnames) as f: self.classnames = f.read().splitlines() self.fns = [] image_names = os.listdir(self.image_dir) @@ -109,7 +105,7 @@ def __getitem__(self, index: int): def __len__(self): return len(self.fns) - def collate_fn(self, batch: List): + def collate_fn(self, batch: list): imgs = torch.stack([s["input"] for s in batch]) img_names = [s["img_name"] for s in batch] ori_sizes = [s["ori_size"] for s in batch] diff --git a/theseus/base/losses/__init__.py b/theseus/base/losses/__init__.py index 48731b2e..8b5348d8 100644 --- a/theseus/base/losses/__init__.py +++ b/theseus/base/losses/__init__.py @@ -5,6 +5,7 @@ from .bce_loss import BCELoss from .ce_loss import * from .focal_loss import FocalLoss +from .mae_loss import MeanAbsoluteErrorLoss from .mse_loss import MeanSquaredErrorLoss from .multi_loss import MultiLoss @@ -14,3 +15,4 @@ LOSS_REGISTRY.register(MeanSquaredErrorLoss) LOSS_REGISTRY.register(ClassificationSmoothCELoss) LOSS_REGISTRY.register(BCELoss) +LOSS_REGISTRY.register(MeanAbsoluteErrorLoss) diff --git a/theseus/base/losses/bce_loss.py b/theseus/base/losses/bce_loss.py index c9c22b4b..b6ad7773 100644 --- a/theseus/base/losses/bce_loss.py +++ b/theseus/base/losses/bce_loss.py @@ -1,4 +1,4 @@ -from typing import Any, Dict +from typing import Any import torch from torch import nn @@ -10,7 +10,7 @@ class BCELoss(nn.Module): r"""CELoss is warper of cross-entropy loss""" def __init__(self, **kwargs): - super(BCELoss, self).__init__() + super().__init__() if "weight" in kwargs: weight = torch.FloatTensor(kwargs.get("weight")) else: @@ -21,8 +21,8 @@ def __init__(self, **kwargs): def forward( self, - outputs: Dict[str, Any], - batch: Dict[str, Any], + outputs: dict[str, Any], + batch: dict[str, Any], device: torch.device = None, ): pred = outputs["outputs"] diff --git a/theseus/base/losses/ce_loss.py b/theseus/base/losses/ce_loss.py index 61b52cf6..a68d0be2 100644 --- a/theseus/base/losses/ce_loss.py +++ b/theseus/base/losses/ce_loss.py @@ -12,7 +12,7 @@ class ClassificationCELoss(nn.Module): r"""CELoss is warper of cross-entropy loss""" def __init__(self, weight: List = None, **kwargs): - super(ClassificationCELoss, self).__init__() + super().__init__() if weight is not None: weight = torch.tensor(weight) self.criterion = nn.CrossEntropyLoss( @@ -46,7 +46,7 @@ class ClassificationSmoothCELoss(nn.Module): r"""SmoothCELoss is warper of label smoothing cross-entropy loss""" def __init__(self, smoothing: float = 0.1, **kwargs): - super(ClassificationSmoothCELoss, self).__init__() + super().__init__() self.smooth_criterion = LabelSmoothingCrossEntropy(smoothing=smoothing) self.soft_criterion = SoftTargetCrossEntropy() @@ -60,9 +60,7 @@ def forward( target = batch["targets"] if pred.shape == target.shape: - loss, loss_dict = self.soft_criterion( - {"outputs": pred}, {"targets": target}, device - ) + loss, loss_dict = self.soft_criterion({"outputs": pred}, {"targets": target}, device) else: # batch["targets"] = batch["targets"].view(-1).contiguous() loss, loss_dict = self.smooth_criterion( diff --git a/theseus/base/losses/focal_loss.py b/theseus/base/losses/focal_loss.py index 84df8bc6..8973d6ef 100644 --- a/theseus/base/losses/focal_loss.py +++ b/theseus/base/losses/focal_loss.py @@ -1,9 +1,11 @@ -from typing import Any, Dict +from typing import Any import torch import torch.nn as nn -from torchvision.ops.focal_loss import sigmoid_focal_loss - +try: + from torchvision.ops.focal_loss import sigmoid_focal_loss +except: + pass from theseus.base.utilities.cuda import move_to @@ -18,8 +20,8 @@ def __init__(self, alpha=0.25, gamma=2, reduction="mean", **kwargs): def forward( self, - outputs: Dict[str, Any], - batch: Dict[str, Any], + outputs: dict[str, Any], + batch: dict[str, Any], device: torch.device = None, ): outputs = outputs["outputs"] @@ -34,8 +36,6 @@ def forward( targets = nn.functional.one_hot(targets, num_classes=num_classes) targets = targets.float().squeeze() - loss = sigmoid_focal_loss( - outputs, targets, self.alpha, self.gamma, self.reduction - ) + loss = sigmoid_focal_loss(outputs, targets, self.alpha, self.gamma, self.reduction) loss_dict = {"L": loss.item()} return loss, loss_dict diff --git a/theseus/base/losses/mae_loss.py b/theseus/base/losses/mae_loss.py new file mode 100644 index 00000000..c764fb6b --- /dev/null +++ b/theseus/base/losses/mae_loss.py @@ -0,0 +1,34 @@ +from typing import Any + +import torch +from torch import nn + +from theseus.base.utilities.cuda import move_to + + +class MeanAbsoluteErrorLoss(nn.Module): + r"""MSELoss is warper of mean absolute error loss""" + + def __init__(self, **kwargs): + super().__init__() + + def forward( + self, + outputs: dict[str, Any], + batch: dict[str, Any], + device: torch.device = None, + ): + pred = outputs["outputs"] + if device is not None: + target = move_to(batch["targets"], device) + else: + target = batch["targets"] + + if pred.shape == target.shape: + loss = torch.mean(torch.abs(pred - target)) + else: + # If the shapes are different, we can use MSELoss + loss = torch.mean(torch.abs(pred.squeeze(-1) - target.view(-1).contiguous())) + + loss_dict = {"MAE": loss.item()} + return loss, loss_dict diff --git a/theseus/base/losses/mse_loss.py b/theseus/base/losses/mse_loss.py index 1144532a..727cb4a7 100644 --- a/theseus/base/losses/mse_loss.py +++ b/theseus/base/losses/mse_loss.py @@ -1,4 +1,4 @@ -from typing import Any, Dict +from typing import Any import torch from torch import nn @@ -10,13 +10,13 @@ class MeanSquaredErrorLoss(nn.Module): r"""MSELoss is warper of mean square error loss""" def __init__(self, **kwargs): - super(MeanSquaredErrorLoss, self).__init__() + super().__init__() self.criterion = nn.MSELoss() def forward( self, - outputs: Dict[str, Any], - batch: Dict[str, Any], + outputs: dict[str, Any], + batch: dict[str, Any], device: torch.device = None, ): pred = outputs["outputs"] @@ -28,7 +28,7 @@ def forward( if pred.shape == target.shape: loss = self.criterion(pred, target) else: - loss = self.criterion(pred, target.view(-1).contiguous()) + loss = self.criterion(pred.squeeze(-1), target.view(-1).contiguous()) loss_dict = {"MSE": loss.item()} return loss, loss_dict diff --git a/theseus/base/losses/multi_loss.py b/theseus/base/losses/multi_loss.py index 5da01a95..e4fba4ca 100644 --- a/theseus/base/losses/multi_loss.py +++ b/theseus/base/losses/multi_loss.py @@ -1,4 +1,5 @@ -from typing import Any, Dict, Iterable +from collections.abc import Iterable +from typing import Any import torch import torch.nn as nn @@ -14,8 +15,8 @@ def __init__(self, losses: Iterable[nn.Module], weights=None, **kwargs): def forward( self, - outputs: Dict[str, Any], - batch: Dict[str, Any], + outputs: dict[str, Any], + batch: dict[str, Any], device: torch.device = None, ): """ diff --git a/theseus/base/losses/smoothing.py b/theseus/base/losses/smoothing.py index 133072a1..34d77a1d 100644 --- a/theseus/base/losses/smoothing.py +++ b/theseus/base/losses/smoothing.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, Iterable +from typing import Any import torch import torch.nn.functional as F @@ -15,15 +15,15 @@ class LabelSmoothingCrossEntropy(nn.Module): """NLL loss with label smoothing.""" def __init__(self, smoothing=0.1): - super(LabelSmoothingCrossEntropy, self).__init__() + super().__init__() assert smoothing < 1.0 self.smoothing = smoothing self.confidence = 1.0 - smoothing def forward( self, - outputs: Dict[str, Any], - batch: Dict[str, Any], + outputs: dict[str, Any], + batch: dict[str, Any], device: torch.device = None, ): @@ -46,12 +46,12 @@ def forward( class SoftTargetCrossEntropy(nn.Module): def __init__(self): - super(SoftTargetCrossEntropy, self).__init__() + super().__init__() def forward( self, - outputs: Dict[str, Any], - batch: Dict[str, Any], + outputs: dict[str, Any], + batch: dict[str, Any], device: torch.device = None, ): diff --git a/theseus/base/metrics/accuracy.py b/theseus/base/metrics/accuracy.py index 89830a89..5c091103 100644 --- a/theseus/base/metrics/accuracy.py +++ b/theseus/base/metrics/accuracy.py @@ -1,4 +1,4 @@ -from typing import Any, Dict +from typing import Any from theseus.base.metrics.metric_template import Metric from theseus.base.utilities.logits import logits2labels @@ -9,28 +9,38 @@ class Accuracy(Metric): Accuracy metric """ - def __init__(self, label_type: str = "multiclass", **kwargs): + def __init__(self, label_type: str = "multiclass", ignore_index=None, **kwargs): super().__init__(**kwargs) self.type = label_type self.threshold = kwargs.get("threshold", 0.5) + self.ignore_index = ignore_index self.reset() - def update(self, outputs: Dict[str, Any], batch: Dict[str, Any]): + def update(self, outputs: dict[str, Any], batch: dict[str, Any]): """ Perform calculation based on prediction and targets """ outputs = outputs["outputs"].detach().cpu() target = batch["targets"].cpu() - prediction = logits2labels( - outputs, label_type=self.type, threshold=self.threshold - ) - - correct = (prediction.view(-1) == target.view(-1)).sum() - - self.total_correct += correct - self.sample_size += prediction.view(-1).size(0) + prediction = logits2labels(outputs, label_type=self.type, threshold=self.threshold) + + # Create mask for non-ignored indices + if self.ignore_index is not None: + mask = target.view(-1) != self.ignore_index + prediction = prediction.view(-1)[mask] + target = target.view(-1)[mask] + else: + prediction = prediction.view(-1) + target = target.view(-1) + + if len(target) > 0: # Avoid division by zero if all targets are ignored + correct = (prediction == target).sum() + self.total_correct += correct + self.sample_size += len(target) def value(self): + if self.sample_size == 0: + return {"acc": 0.0} return {"acc": (self.total_correct / self.sample_size).item()} def reset(self): diff --git a/theseus/base/metrics/bl_accuracy.py b/theseus/base/metrics/bl_accuracy.py index fc56c61c..6aee9852 100644 --- a/theseus/base/metrics/bl_accuracy.py +++ b/theseus/base/metrics/bl_accuracy.py @@ -1,4 +1,4 @@ -from typing import Any, Dict +from typing import Any import numpy as np from sklearn.metrics import balanced_accuracy_score @@ -23,13 +23,14 @@ class BalancedAccuracyMetric(Metric): Balanced Accuracy metric for classification """ - def __init__(self, label_type: str = "multiclass", **kwargs): + def __init__(self, label_type: str = "multiclass", ignore_index=None, **kwargs): super().__init__(**kwargs) self.type = label_type self.threshold = kwargs.get("threshold", 0.5) + self.ignore_index = ignore_index self.reset() - def update(self, outputs: Dict[str, Any], batch: Dict[str, Any]): + def update(self, outputs: dict[str, Any], batch: dict[str, Any]): """ Perform calculation based on prediction and targets """ @@ -40,6 +41,13 @@ def update(self, outputs: Dict[str, Any], batch: Dict[str, Any]): outputs = outputs.detach().cpu() targets = targets.detach().cpu().view(-1) + # Filter out ignored indices + if self.ignore_index is not None: + mask = targets != self.ignore_index + outputs = outputs[mask] + targets = targets[mask] + + # Convert to lists and append to accumulated results self.outputs += outputs.numpy().tolist() self.targets += targets.numpy().tolist() @@ -51,6 +59,9 @@ def get_all_unique_id(self): self.unique_ids = np.unique(self.targets) def value(self): + if len(self.targets) == 0: + return {"bl_acc": 0.0} + self.get_all_unique_id() self.corrects = {str(k): 0 for k in self.unique_ids} @@ -70,6 +81,6 @@ def value(self): ] # Get mean accuracy across classes - values = sum(each_acc) / len(self.unique_ids) + values = sum(each_acc) / len(self.unique_ids) if self.unique_ids.size > 0 else 0.0 return {"bl_acc": values} diff --git a/theseus/base/metrics/confusion_matrix.py b/theseus/base/metrics/confusion_matrix.py index 4efd8203..ce99a8e7 100644 --- a/theseus/base/metrics/confusion_matrix.py +++ b/theseus/base/metrics/confusion_matrix.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Optional +from typing import Any import matplotlib.pyplot as plt import numpy as np @@ -12,7 +12,7 @@ LOGGER = LoggerObserver.getLogger("main") -def plot_cfm(cm, ax, labels: List): +def plot_cfm(cm, ax, labels: list): """ Make confusion matrix figure labels: `Optional[List]` @@ -28,7 +28,7 @@ def plot_cfm(cm, ax, labels: List): ax.yaxis.set_ticklabels(labels, rotation=0) -def make_cm_fig(cms, labels: Optional[List] = None): +def make_cm_fig(cms, labels: list | None = None): if cms.shape[0] > 1: # multilabel num_classes = cms.shape[0] @@ -76,7 +76,7 @@ def __init__(self, classnames=None, label_type: str = "multiclass", **kwargs): self.threshold = kwargs.get("threshold", 0.5) self.reset() - def update(self, outputs: Dict[str, Any], batch: Dict[str, Any]): + def update(self, outputs: dict[str, Any], batch: dict[str, Any]): """ Perform calculation based on prediction and targets """ diff --git a/theseus/base/metrics/f1.py b/theseus/base/metrics/f1.py index 9ca85af0..c5432305 100644 --- a/theseus/base/metrics/f1.py +++ b/theseus/base/metrics/f1.py @@ -1,4 +1,4 @@ -from typing import Any, Dict +from typing import Any from sklearn.metrics import f1_score @@ -11,14 +11,17 @@ class F1ScoreMetric(Metric): F1 Score Metric (including macro, micro) """ - def __init__(self, average="weighted", label_type: str = "multiclass", **kwargs): + def __init__( + self, average="weighted", label_type: str = "multiclass", ignore_index=None, **kwargs + ): super().__init__(**kwargs) self.average = average self.type = label_type self.threshold = kwargs.get("threshold", 0.5) + self.ignore_index = ignore_index self.reset() - def update(self, outputs: Dict[str, Any], batch: Dict[str, Any]): + def update(self, outputs: dict[str, Any], batch: dict[str, Any]): """ Perform calculation based on prediction and targets """ @@ -27,10 +30,18 @@ def update(self, outputs: Dict[str, Any], batch: Dict[str, Any]): outputs = logits2labels(outputs, label_type=self.type, threshold=self.threshold) + # Filter out ignored indices + if self.ignore_index is not None: + mask = targets != self.ignore_index + outputs = outputs[mask] + targets = targets[mask] + self.preds += outputs.numpy().tolist() self.targets += targets.numpy().tolist() def value(self): + if len(self.targets) == 0: + return {f"{self.average}-f1": 0.0} score = f1_score(self.targets, self.preds, average=self.average) return {f"{self.average}-f1": score} diff --git a/theseus/base/metrics/mcc.py b/theseus/base/metrics/mcc.py index dc1cb43b..3d4af3bc 100644 --- a/theseus/base/metrics/mcc.py +++ b/theseus/base/metrics/mcc.py @@ -1,4 +1,4 @@ -from typing import Any, Dict +from typing import Any from sklearn.metrics import matthews_corrcoef @@ -11,12 +11,13 @@ class MCC(Metric): Mathew Correlation Coefficient """ - def __init__(self, label_type: str = "multiclass", **kwargs): + def __init__(self, label_type: str = "multiclass", ignore_index=None, **kwargs): super().__init__(**kwargs) self.type = label_type + self.ignore_index = ignore_index self.reset() - def update(self, outputs: Dict[str, Any], batch: Dict[str, Any]): + def update(self, outputs: dict[str, Any], batch: dict[str, Any]): """ Perform calculation based on prediction and targets """ @@ -24,13 +25,21 @@ def update(self, outputs: Dict[str, Any], batch: Dict[str, Any]): outputs = outputs["outputs"].detach().cpu() outputs = logits2labels(outputs, label_type=self.type) + # Filter out ignored indices + if self.ignore_index is not None: + mask = targets != self.ignore_index + outputs = outputs[mask] + targets = targets[mask] + self.preds += outputs.numpy().tolist() self.targets += targets.numpy().tolist() def value(self): + if len(self.targets) == 0: + return {"mcc": 0.0} score = matthews_corrcoef(self.targets, self.preds) return { - f"mcc": score, + "mcc": score, } def reset(self): diff --git a/theseus/base/metrics/metric_template.py b/theseus/base/metrics/metric_template.py index 98cc8ae5..e7e6d23f 100644 --- a/theseus/base/metrics/metric_template.py +++ b/theseus/base/metrics/metric_template.py @@ -1,20 +1,55 @@ -class Metric: - """Abstract metric class""" +from abc import ABC, abstractmethod +from typing import Any - def __init__(self, **kwargs): + +class Metric(ABC): + """ + Abstract base class for all metrics in Theseus. + + Subclasses must implement ``update()``, ``value()``, ``reset()``, + ``summary()``, and ``__str__()``. + + Example:: + + @METRIC_REGISTRY.register() + class MyAccuracy(Metric): + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.correct = 0 + self.total = 0 + + def update(self, outputs, batch): + ... + + def value(self): + return {"accuracy": self.correct / max(self.total, 1)} + + def reset(self): + self.correct = 0 + self.total = 0 + """ + + def __init__(self, **kwargs: Any) -> None: pass - def update(self): - raise NotImplementedError() + @abstractmethod + def update(self, outputs: dict[str, Any], batch: dict[str, Any]) -> None: + """Update metric state with new predictions and targets.""" + ... - def value(self): - raise NotImplementedError() + @abstractmethod + def value(self) -> dict[str, Any]: + """Compute and return the metric value(s) as a dict.""" + ... - def reset(self): - raise NotImplementedError() + @abstractmethod + def reset(self) -> None: + """Reset metric state for next epoch.""" + ... - def summary(self): - raise NotImplementedError() + def summary(self) -> str: + """Return a human-readable summary of the metric.""" + return str(self.value()) - def __str__(self): - raise NotImplementedError() + def __str__(self) -> str: + return f"{self.__class__.__name__}: {self.summary()}" diff --git a/theseus/base/metrics/precision_recall.py b/theseus/base/metrics/precision_recall.py index 92234bf3..ebe56af3 100644 --- a/theseus/base/metrics/precision_recall.py +++ b/theseus/base/metrics/precision_recall.py @@ -1,4 +1,4 @@ -from typing import Any, Dict +from typing import Any from sklearn.metrics import precision_score, recall_score @@ -11,31 +11,42 @@ class PrecisionRecall(Metric): F1 Score Metric (including macro, micro) """ - def __init__(self, average="weighted", label_type: str = "multiclass", **kwargs): + def __init__( + self, average="weighted", label_type: str = "multiclass", ignore_index=None, **kwargs + ): super().__init__(**kwargs) self.average = average self.type = label_type self.threshold = kwargs.get("threshold", 0.5) + self.ignore_index = ignore_index self.reset() - def update(self, outputs: Dict[str, Any], batch: Dict[str, Any]): + def update(self, outputs: dict[str, Any], batch: dict[str, Any]): """ Perform calculation based on prediction and targets """ - targets = batch["targets"].cpu() + targets = batch["targets"].cpu().view(-1) outputs = outputs["outputs"].detach().cpu() outputs = logits2labels(outputs, label_type=self.type, threshold=self.threshold) + # Filter out ignored indices + if self.ignore_index is not None: + mask = targets != self.ignore_index + outputs = outputs[mask] + targets = targets[mask] + self.preds += outputs.numpy().tolist() self.targets += targets.numpy().tolist() def value(self): - precision = precision_score( - self.targets, self.preds, average=self.average, zero_division=1 - ) - recall = recall_score( - self.targets, self.preds, average=self.average, zero_division=1 - ) + if len(self.targets) == 0: + return { + f"{self.average}-precision": 0.0, + f"{self.average}-recall": 0.0, + } + + precision = precision_score(self.targets, self.preds, average=self.average, zero_division=1) + recall = recall_score(self.targets, self.preds, average=self.average, zero_division=1) return { f"{self.average}-precision": precision, f"{self.average}-recall": recall, diff --git a/theseus/base/metrics/roc_auc_score.py b/theseus/base/metrics/roc_auc_score.py index ef3bca31..a606b6c0 100644 --- a/theseus/base/metrics/roc_auc_score.py +++ b/theseus/base/metrics/roc_auc_score.py @@ -1,4 +1,4 @@ -from typing import Any, Dict +from typing import Any import torch @@ -8,10 +8,11 @@ has_scikitplot = True except: has_scikitplot = False -from sklearn.metrics import roc_auc_score +import numpy as np +from sklearn.metrics import auc, precision_recall_curve, roc_auc_score from theseus.base.metrics.metric_template import Metric -from theseus.base.utilities.cuda import detach, move_to +from theseus.base.utilities.cuda import move_to from theseus.base.utilities.loggers.observer import LoggerObserver from theseus.base.utilities.logits import logits2labels @@ -35,16 +36,14 @@ def __init__( self.average = average self.plot_curve = plot_curve - if self.type == "multiclass": - self.label_type = "ovr" - elif self.type == "multilabel": + if self.type == "multiclass" or self.type == "multilabel": self.label_type = "ovr" else: self.label_type = "raise" self.reset() - def update(self, outputs: Dict[str, Any], batch: Dict[str, Any]): + def update(self, outputs: dict[str, Any], batch: dict[str, Any]): """ Perform calculation based on prediction and targets """ @@ -67,14 +66,41 @@ def value(self): average=self.average, multi_class=self.label_type, ) - except Exception as e: - LOGGER.text( - f"AUC score could not be calculated: {e}", level=LoggerObserver.WARN - ) - roc_auc_scr = 0 + except Exception: + try: + preds = np.array(self.preds) + if preds.ndim == 2: + preds = preds[:, 1] + roc_auc_scr = roc_auc_score( + self.targets, + preds, + average=self.average, + multi_class=self.label_type, + ) + except Exception as e: + LOGGER.text(f"AUC score could not be calculated: {e}", level=LoggerObserver.WARN) + roc_auc_scr = 0 + + try: + precision, recall, _ = precision_recall_curve(self.targets, self.preds, pos_label=1) + pr_auc = auc(recall, precision) + except Exception: + try: + preds = np.array(self.preds) + if preds.ndim == 2: + preds = preds[:, 1] + precision, recall, _ = precision_recall_curve(self.targets, preds, pos_label=1) + pr_auc = auc(recall, precision) + except Exception as e: + LOGGER.text( + f"Precision-Recall AUC score could not be calculated: {e}", + level=LoggerObserver.WARN, + ) + pr_auc = 0 results = { f"{self.average}-roc_auc_score": roc_auc_scr, + "pr_auc_score": pr_auc, } if has_scikitplot and self.plot_curve: roc_curve_fig = plot_roc(self.targets, self.preds).get_figure() diff --git a/theseus/base/models/wrapper.py b/theseus/base/models/wrapper.py index 352a7d4f..6ae63ebd 100644 --- a/theseus/base/models/wrapper.py +++ b/theseus/base/models/wrapper.py @@ -1,9 +1,13 @@ -from typing import Any, Callable, Dict, List, Mapping, Optional, Union +from __future__ import annotations + +from collections.abc import Mapping +from typing import Any import lightning.pytorch as pl import torch import torch.nn as nn -from lightning.pytorch.utilities.types import _METRIC, STEP_OUTPUT +from lightning.pytorch.utilities.types import STEP_OUTPUT +from torch.amp import autocast from theseus.base.datasets import LightningDataModuleWrapper from theseus.base.optimizers import OPTIM_REGISTRY, SCHEDULER_REGISTRY @@ -12,20 +16,24 @@ class LightningModelWrapper(pl.LightningModule): """ - Wrapper for Lightning Module - Instansiates the model, criterion, optimizer and scheduler + Lightning wrapper that bridges Theseus components to the Lightning training loop. + + Encapsulates model, criterion, optimizer, scheduler, and metrics into a single + ``pl.LightningModule`` with proper step methods and automatic mixed precision. """ def __init__( self, model: nn.Module, - criterion: nn.Module = None, - metrics: List[Any] = None, - optimizer_config: Dict = None, - scheduler_config: Dict = None, - scheduler_kwargs: Dict = None, - datamodule: LightningDataModuleWrapper = None, - ): + criterion: nn.Module | None = None, + *, + metrics: list[Any] | None = None, + optimizer_config: dict | None = None, + scheduler_config: dict | None = None, + scheduler_kwargs: dict | None = None, + datamodule: LightningDataModuleWrapper | None = None, + use_mixed_precision: bool = False, + ) -> None: super().__init__() self.model = model self.criterion = criterion @@ -34,10 +42,12 @@ def __init__( self.scheduler_config = scheduler_config self.scheduler_kwargs = scheduler_kwargs self.datamodule = datamodule - self.lr = 0 - self.metric_dict = {} + self.use_mixed_precision = use_mixed_precision + self.lr: float = 0.0 + self.metric_dict: dict[str, Any] = {} - def log_dict(self, dictionary: Mapping[str, Any], **kwargs) -> None: + def log_dict(self, dictionary: Mapping[str, Any], **kwargs: Any) -> None: + """Filter non-loggable values before passing to Lightning's log_dict.""" filtered_dict = { key: value for key, value in dictionary.items() @@ -45,85 +55,111 @@ def log_dict(self, dictionary: Mapping[str, Any], **kwargs) -> None: } return super().log_dict(filtered_dict, **kwargs) - def on_train_batch_end( - self, outputs: STEP_OUTPUT, batch: Any, batch_idx: int - ) -> None: - lrl = [x["lr"] for x in self.optimizer.param_groups] - self.lr = sum(lrl) / len(lrl) - - def on_validation_epoch_end(self) -> None: + @property + def _autocast_device(self) -> str: + """Detect the correct device type for autocast.""" + if self.device.type == "cuda": + return "cuda" + elif self.device.type == "mps": + return "mps" + return "cpu" + + def on_train_batch_end(self, outputs: STEP_OUTPUT, batch: Any, batch_idx: int) -> None: + # Use Lightning's optimizers() API instead of storing self.optimizer + optimizers = self.optimizers() + if optimizers is not None: + opt = optimizers if not isinstance(optimizers, list) else optimizers[0] + lrl = [x["lr"] for x in opt.param_groups] + self.lr = sum(lrl) / len(lrl) + + def _compute_and_log_metrics(self, batch_size_key: str = "valloader") -> None: + """Compute metrics and log them. Reduces duplication between val/test.""" self.metric_dict = {} if self.metrics is not None: for metric in self.metrics: self.metric_dict.update(metric.value()) metric.reset() - self.log_dict( - self.metric_dict, - prog_bar=True, - batch_size=self.datamodule.valloader.batch_size, - ) + loader = getattr(self.datamodule, batch_size_key, None) + batch_size = loader.batch_size if loader is not None else 1 + self.log_dict(self.metric_dict, prog_bar=True, batch_size=batch_size) - def on_test_epoch_end(self) -> None: - self.metric_dict = {} - if self.metrics is not None: - for metric in self.metrics: - self.metric_dict.update(metric.value()) - metric.reset() + def on_validation_epoch_end(self) -> None: + self._compute_and_log_metrics("valloader") - self.log_dict( - self.metric_dict, - prog_bar=True, - batch_size=self.datamodule.testloader.batch_size, - ) + def on_test_epoch_end(self) -> None: + self._compute_and_log_metrics("testloader") - def _forward(self, batch: Dict, metrics: List[Any] = None): + def _forward( + self, + batch: dict[str, Any], + metrics: list[Any] | None = None, + ) -> dict[str, Any]: """ - Forward the batch through models, losses and metrics - If some parameters are needed, it's best to include in the batch + Forward the batch through models, losses and metrics. + If some parameters are needed, it's best to include in the batch. """ - - outputs = self.model.forward_batch(batch) - loss, loss_dict = self.criterion(outputs, batch) - - if metrics is not None: - for metric in metrics: - metric.update(outputs, batch) + device_type = self._autocast_device + + # BF16 is not supported on all devices, and autocast sometimes fails on CPU + # if not explicitly supported. We'll be more conservative here. + enabled = self.use_mixed_precision + if device_type == "cpu" and enabled: + # Most CPUs don't support BF16/FP16 well in autocast unless using specific CPUs + # It's safer to disable for CPU unless it's explicitly managed by Lightning + enabled = False + + with autocast(device_type=device_type, enabled=enabled): + outputs = self.model.forward_batch(batch) + if self.criterion is None: + loss = outputs["outputs"].get("loss", None) + loss_dict = outputs["outputs"].get("loss_dict", None) + if loss is None or loss_dict is None: + raise ValueError( + "No loss found in model outputs. Please ensure the model returns a loss." + ) + else: + loss, loss_dict = self.criterion(outputs, batch) + + if metrics is not None: + for metric in metrics: + metric.update(outputs, batch) return {"loss": loss, "loss_dict": loss_dict, "model_outputs": outputs} - def trainable_parameters(self): + def trainable_parameters(self) -> int: + """Return the number of trainable parameters.""" return sum(p.numel() for p in self.parameters() if p.requires_grad) - def training_step(self, batch, batch_idx): - # training_step defines the train loop. + def training_step(self, batch: Any, batch_idx: int) -> dict[str, Any]: outputs = self._forward(batch) self.log_dict(outputs["loss_dict"], prog_bar=True, on_step=True, on_epoch=False) return outputs - def validation_step(self, batch, batch_idx): - # this is the validation loop + def validation_step(self, batch: Any, batch_idx: int) -> dict[str, Any]: outputs = self._forward(batch, metrics=self.metrics) self.log_dict(outputs["loss_dict"], prog_bar=True, on_step=True, on_epoch=False) return outputs - def test_step(self, batch, batch_idx): - # this is the test loop + def test_step(self, batch: Any, batch_idx: int) -> dict[str, Any]: outputs = self._forward(batch, metrics=self.metrics) self.log_dict(outputs["loss_dict"], prog_bar=True, on_step=True, on_epoch=False) return outputs - def predict_step(self, batch, batch_idx=None): - pred = self.model.get_prediction(batch) - return pred + def predict_step(self, batch: Any, batch_idx: int | None = None) -> Any: + return self.model.get_prediction(batch) - def configure_optimizers(self): + def configure_optimizers(self) -> Any: if self.optimizer_config is not None: self.optimizer = get_instance( self.optimizer_config, registry=OPTIM_REGISTRY, params=self.model.parameters(), ) + else: + from torch.optim import AdamW + + self.optimizer = AdamW(self.parameters(), lr=self.lr) if self.scheduler_config is not None: self.scheduler = get_instance( @@ -132,12 +168,40 @@ def configure_optimizers(self): optimizer=self.optimizer, **self.scheduler_kwargs, ) + + scheduler_interval = "epoch" if self.scheduler.step_per_epoch else "step" + scheduler = { + "scheduler": self.scheduler.scheduler, + "interval": scheduler_interval, + } + return [self.optimizer], [scheduler] else: - return self.optimizer + from torch.optim.lr_scheduler import LinearLR, SequentialLR - scheduler_interval = "epoch" if self.scheduler.step_per_epoch else "step" - scheduler = { - "scheduler": self.scheduler.scheduler, - "interval": scheduler_interval, - } - return [self.optimizer], [scheduler] + n_steps = self.trainer.estimated_stepping_batches + n_warmup_steps = int(0.1 * n_steps) + n_decay_steps = int(0.9 * n_steps) + + warmup = LinearLR( + self.optimizer, + start_factor=0.01, + end_factor=1.0, + total_iters=n_warmup_steps, + ) + decay = LinearLR( + self.optimizer, + start_factor=1.0, + end_factor=0.01, + total_iters=n_decay_steps, + ) + self.scheduler = SequentialLR( + optimizer=self.optimizer, + schedulers=[warmup, decay], + milestones=[n_warmup_steps], + ) + + scheduler = { + "scheduler": self.scheduler, + "interval": "step", + } + return [self.optimizer], [scheduler] diff --git a/theseus/base/optimizers/schedulers/cosine.py b/theseus/base/optimizers/schedulers/cosine.py index 47542ffa..43c04802 100644 --- a/theseus/base/optimizers/schedulers/cosine.py +++ b/theseus/base/optimizers/schedulers/cosine.py @@ -1,6 +1,6 @@ # code from AllenNLP -from typing import Any, Dict +from typing import Any import numpy as np import torch @@ -91,9 +91,7 @@ def __init__( if last_epoch == -1: for i, group in enumerate(self.optimizer.param_groups): if self.param_group_field not in group: - raise KeyError( - f"{self.param_group_field} missing from param_groups[{i}]" - ) + raise KeyError(f"{self.param_group_field} missing from param_groups[{i}]") group.setdefault( self._initial_param_group_field, group[self.param_group_field], @@ -105,20 +103,17 @@ def __init__( f"{self._initial_param_group_field} missing from param_groups[{i}]" ) self.base_values = [ - group[self._initial_param_group_field] - for group in self.optimizer.param_groups + group[self._initial_param_group_field] for group in self.optimizer.param_groups ] self.last_epoch = last_epoch - def state_dict(self) -> Dict[str, Any]: + def state_dict(self) -> dict[str, Any]: """ Returns the state of the scheduler as a `dict`. """ - return { - key: value for key, value in self.__dict__.items() if key != "optimizer" - } + return {key: value for key, value in self.__dict__.items() if key != "optimizer"} - def load_state_dict(self, state_dict: Dict[str, Any]) -> None: + def load_state_dict(self, state_dict: dict[str, Any]) -> None: """ Load the schedulers state. # Parameters @@ -146,12 +141,7 @@ def get_values(self): lrs = [ self.eta_min + ((lr - self.eta_min) / 2) - * ( - np.cos( - np.pi * (self._cycle_counter % self._cycle_len) / self._cycle_len - ) - + 1 - ) + * (np.cos(np.pi * (self._cycle_counter % self._cycle_len) / self._cycle_len) + 1) for lr in base_lrs ] diff --git a/theseus/base/optimizers/schedulers/wrapper.py b/theseus/base/optimizers/schedulers/wrapper.py index 3f5d23b5..3faf09b8 100644 --- a/theseus/base/optimizers/schedulers/wrapper.py +++ b/theseus/base/optimizers/schedulers/wrapper.py @@ -1,13 +1,11 @@ import math from torch.optim.lr_scheduler import ( - CosineAnnealingLR, CosineAnnealingWarmRestarts, LambdaLR, MultiStepLR, OneCycleLR, ReduceLROnPlateau, - StepLR, ) from .cosine import CosineWithRestarts @@ -24,9 +22,7 @@ def __init__(self, optimizer, scheduler_name, **kwargs) -> None: def one_cycle(y1=0.0, y2=1.0, steps=100): # lambda function for sinusoidal ramp from y1 to y2 - return ( - lambda x: ((1 - math.cos(x * math.pi / steps)) / 2) * (y2 - y1) + y1 - ) + return lambda x: ((1 - math.cos(x * math.pi / steps)) / 2) * (y2 - y1) + y1 lf = one_cycle(1, 0.2, kwargs["num_epochs"]) # cosine 1->hyp['lrf'] scheduler = LambdaLR(optimizer, lr_lambda=lf) diff --git a/theseus/base/pipeline.py b/theseus/base/pipeline.py index 9bc0e71d..fefe7fac 100644 --- a/theseus/base/pipeline.py +++ b/theseus/base/pipeline.py @@ -1,5 +1,8 @@ +from __future__ import annotations + import os from datetime import datetime +from typing import Any import torch from omegaconf import DictConfig, OmegaConf @@ -20,21 +23,56 @@ from theseus.base.utilities.getter import get_instance, get_instance_recursively from theseus.base.utilities.loggers import FileLogger, ImageWriter, LoggerObserver from theseus.base.utilities.seed import seed_everything +import torch -class BasePipeline(object): - """docstring for BasePipeline.""" +class _PipelineBase: + """ + Shared base for train/test pipelines, eliminating duplication of + globals initialization, registry setup, and logging. + """ - def __init__(self, opt: DictConfig): - super(BasePipeline, self).__init__() + def __init__(self, opt: DictConfig) -> None: self.opt = opt self.seed = self.opt["global"].get("seed", 1702) seed_everything(self.seed) + self._initialized = False + + @property + def initialized(self) -> bool: + return self._initialized + + @initialized.setter + def initialized(self, value: bool) -> None: + self._initialized = value + + def _log(self, msg: str, level: int = LoggerObserver.INFO) -> None: + """Convenience logging helper.""" + self.logger.text(msg, level=level) + + def _setup_savedir(self) -> str: + """Create and return the experiment save directory.""" + exp_name = self.opt["global"].get("exp_name", None) + exist_ok = self.opt["global"].get("exist_ok", False) + save_dir = self.opt["global"].get("save_dir", "runs") + + if exp_name: + savedir = os.path.join(save_dir, exp_name) + if not exist_ok: + savedir = get_new_folder_name(savedir) + else: + savedir = os.path.join( + save_dir, + datetime.now().strftime("%Y-%m-%d_%H-%M-%S"), + ) + os.makedirs(savedir, exist_ok=True) + return savedir - self.initialized = False + def init_globals(self) -> None: + """Initialize logger, experiment directory, and global variables.""" + # Set float32 matmul precision for Tensor Cores (e.g., A100) + torch.set_float32_matmul_precision("medium") - def init_globals(self): - # Main Loggers self.logger = LoggerObserver.getLogger("main") # Global variables @@ -43,47 +81,61 @@ def init_globals(self): self.debug = self.opt["global"].get("debug", False) self.resume = self.opt["global"].get("resume", None) self.pretrained = self.opt["global"].get("pretrained", None) - self.transform_cfg = self.opt["global"].get("cfg_transform", None) - # Experiment name - if self.exp_name: - self.savedir = os.path.join( - self.opt["global"].get("save_dir", "runs"), self.exp_name - ) - if not self.exist_ok: - self.savedir = get_new_folder_name(self.savedir) - else: - self.savedir = os.path.join( - self.opt["global"].get("save_dir", "runs"), - datetime.now().strftime("%Y-%m-%d_%H-%M-%S"), - ) - os.makedirs(self.savedir, exist_ok=True) + # Setup save directory + self.savedir = self._setup_savedir() - # Logging to files + # File logging file_logger = FileLogger(__name__, self.savedir, debug=self.debug) self.logger.subscribe(file_logger) - # Logging images + # Image logging image_logger = ImageWriter(self.savedir) self.logger.subscribe(image_logger) + # Transform config self.transform_cfg = self.opt.get("augmentations", None) - # Logging out configs - self.logger.text("\n" + OmegaConf.to_yaml(self.opt), level=LoggerObserver.INFO) - self.logger.text( - f"Everything will be saved to {self.savedir}", - level=LoggerObserver.INFO, + # Log config + self._log("\n" + OmegaConf.to_yaml(self.opt)) + self._log(f"Everything will be saved to {self.savedir}") + + def init_registry(self) -> None: + """Initialize component registries. Override in subclass to extend.""" + self.model_registry = MODEL_REGISTRY + self.dataset_registry = DATASET_REGISTRY + self.dataloader_registry = DATALOADER_REGISTRY + self.metric_registry = METRIC_REGISTRY + self.loss_registry = LOSS_REGISTRY + self.callbacks_registry = CALLBACKS_REGISTRY + self.trainer_registry = TRAINER_REGISTRY + self.transform_registry = TRANSFORM_REGISTRY + self._log( + "You should override the init_registry() function", + LoggerObserver.CRITICAL, ) - def init_train_dataloader(self): - # DataLoaders + +class BasePipeline(_PipelineBase): + """ + Full training pipeline that orchestrates all components: + globals → registry → data → model → callbacks → trainer. + + Subclass this and override ``init_registry()`` to plug in task-specific + registries. + """ + + def __init__(self, opt: DictConfig) -> None: + super().__init__(opt) + + def _init_transforms(self) -> dict[str, Any]: + """Initialize transforms, returning a dict with 'train'/'val' keys.""" if self.transform_cfg is not None: - self.transform = get_instance_recursively( - self.transform_cfg, registry=self.transform_registry - ) - else: - self.transform = {"train": None, "val": None} + return get_instance_recursively(self.transform_cfg, registry=self.transform_registry) + return {"train": None, "val": None} + + def init_train_dataloader(self) -> None: + self.transform = self._init_transforms() self.train_dataset = get_instance_recursively( self.opt["data"]["dataset"]["train"], @@ -96,66 +148,14 @@ def init_train_dataloader(self): dataset=self.train_dataset, ) - self.logger.text( - f"Number of training samples: {len(self.train_dataset)}", - level=LoggerObserver.INFO, - ) - self.logger.text( - f"Number of training iterations each epoch: {len(self.train_dataloader)}", - level=LoggerObserver.INFO, - ) + self._log(f"Number of training samples: {len(self.train_dataset)}") + self._log(f"Number of training iterations each epoch: {len(self.train_dataloader)}") - def init_validation_dataloader(self): - - # DataLoaders - if self.transform_cfg is not None: - self.transform = get_instance_recursively( - self.transform_cfg, registry=self.transform_registry - ) - else: - self.transform = {"train": None, "val": None} + def init_validation_dataloader(self) -> None: + self.transform = self._init_transforms() if self.opt["data"]["dataset"].get("val", None) is None: - split_ratio = self.opt.data.get("auto_split_ratio", 0.8) - self.logger.text( - f"No validation dataset found. Auto splitting training dataset with ratio={split_ratio}.", - level=LoggerObserver.WARN, - ) - train_size = int(len(self.train_dataset)) - val_size = int(train_size * (1 - split_ratio)) - train_dataset, val_dataset = ( - Subset(self.train_dataset, indices=indices) - for indices in torch.split_with_sizes( - torch.arange(train_size), [train_size - val_size, val_size] - ) - ) - attrs = dir(self.train_dataset) - for attr in attrs: - if not attr.startswith("__"): - setattr(train_dataset, attr, getattr(self.train_dataset, attr)) - setattr(val_dataset, attr, getattr(self.train_dataset, attr)) - - # collate_fn = getattr(self.train_dataset, "collate_fn", None) - # setattr(train_dataset, "collate_fn", collate_fn) - # setattr(val_dataset, "collate_fn", collate_fn) - self.train_dataset = train_dataset - self.val_dataset = val_dataset - - self.train_dataloader = get_instance_recursively( - self.opt["data"]["dataloader"]["train"], - registry=self.dataloader_registry, - dataset=self.train_dataset, - ) - - self.logger.text( - f"Number of training samples: {len(self.train_dataset)}", - level=LoggerObserver.INFO, - ) - self.logger.text( - f"Number of training iterations each epoch: {len(self.train_dataloader)}", - level=LoggerObserver.INFO, - ) - + self._auto_split_dataset() else: self.val_dataset = get_instance_recursively( self.opt["data"]["dataset"]["val"], @@ -170,52 +170,98 @@ def init_validation_dataloader(self): ) self.classnames = getattr(self.val_dataset, "classnames", None) - self.logger.text( - f"Number of validation samples: {len(self.val_dataset)}", - level=LoggerObserver.INFO, + self._log(f"Number of validation samples: {len(self.val_dataset)}") + self._log(f"Number of validation iterations each epoch: {len(self.val_dataloader)}") + + def _auto_split_dataset(self) -> None: + """Auto-split training dataset when no validation set is provided.""" + split_ratio = self.opt.data.get("auto_split_ratio", 0.8) + self._log( + f"No validation dataset found. Auto splitting training dataset " + f"with ratio={split_ratio}.", + level=LoggerObserver.WARN, ) - self.logger.text( - f"Number of validation iterations each epoch: {len(self.val_dataloader)}", - level=LoggerObserver.INFO, + train_size = len(self.train_dataset) + val_size = int(train_size * (1 - split_ratio)) + + train_dataset, val_dataset = ( + Subset(self.train_dataset, indices=indices) + for indices in torch.split_with_sizes( + torch.arange(train_size), [train_size - val_size, val_size] + ) + ) + + # Copy attributes from original dataset to subsets + attrs = dir(self.train_dataset) + for attr in attrs: + if not attr.startswith("__"): + setattr(train_dataset, attr, getattr(self.train_dataset, attr)) + setattr(val_dataset, attr, getattr(self.train_dataset, attr)) + + self.train_dataset = train_dataset + self.val_dataset = val_dataset + + self.train_dataloader = get_instance_recursively( + self.opt["data"]["dataloader"]["train"], + registry=self.dataloader_registry, + dataset=self.train_dataset, ) - def init_test_dataloader(self): - # Transforms & Datasets + self._log(f"Number of training samples: {len(self.train_dataset)}") + self._log(f"Number of training iterations each epoch: {len(self.train_dataloader)}") + + def init_test_dataloader(self) -> None: + """Initialize test dataset and dataloader, falling back to val if test is missing.""" self.transform = get_instance_recursively( self.transform_cfg, registry=self.transform_registry ) + transform_cfg = ( + self.transform["test"] if "test" in self.transform else self.transform.get("val", None) + ) + + test_data_cfg = self.opt["data"]["dataset"].get("test", None) + if test_data_cfg is None: + self._log( + "No test dataset found in config. Falling back to val dataset.", + level=LoggerObserver.WARN, + ) + test_data_cfg = self.opt["data"]["dataset"].get("val") + self.test_dataset = get_instance_recursively( - self.opt["data"]["dataset"]["test"], + test_data_cfg, registry=self.dataset_registry, - transform=self.transform.get("test", "val"), + transform=transform_cfg, ) + + test_loader_cfg = self.opt["data"]["dataloader"].get("test", None) + if test_loader_cfg is None: + self._log( + "No test dataloader found in config. Falling back to val dataloader.", + level=LoggerObserver.WARN, + ) + test_loader_cfg = self.opt["data"]["dataloader"].get("val") + self.test_dataloader = get_instance_recursively( - self.opt["data"]["dataloader"]["test"], + test_loader_cfg, registry=self.dataloader_registry, dataset=self.test_dataset, ) - self.classnames = getattr(self.test_dataloader, "classnames", None) + self.classnames = getattr(self.test_dataset, "classnames", None) - self.logger.text( - f"Number of test samples: {len(self.test_dataset)}", - level=LoggerObserver.INFO, - ) - self.logger.text( - f"Number of test iterations each epoch: {len(self.test_dataloader)}", - level=LoggerObserver.INFO, - ) + self._log(f"Number of test samples: {len(self.test_dataset)}") + self._log(f"Number of test iterations each epoch: {len(self.test_dataloader)}") - def init_datamodule(self): + def init_datamodule(self) -> None: self.datamodule = LightningDataModuleWrapper( trainloader=getattr(self, "train_dataloader", None), valloader=getattr(self, "val_dataloader", None), testloader=getattr(self, "test_dataloader", None), ) - def init_model(self): - CLASSNAMES = self.classnames + def init_model(self) -> Any: + CLASSNAMES = getattr(self, "classnames", None) model = get_instance( self.opt["model"], registry=self.model_registry, @@ -224,8 +270,11 @@ def init_model(self): ) return model - def init_criterion(self): - CLASSNAMES = self.classnames + def init_criterion(self) -> Any | None: + if self.opt["loss"] is None: + return None + + CLASSNAMES = getattr(self, "classnames", None) self.criterion = get_instance_recursively( self.opt["loss"], registry=self.loss_registry, @@ -234,19 +283,22 @@ def init_criterion(self): ) return self.criterion - def init_model_with_loss(self, is_train=True): + def init_model_with_loss(self, is_train: bool = True) -> None: self.model = self.init_model() criterion = self.init_criterion() num_epochs = self.opt["trainer"]["args"]["max_epochs"] batch_size = self.opt["data"]["dataloader"]["val"]["args"]["batch_size"] + use_mixed_precision = self.opt["trainer"]["args"].get("precision", None) + use_mixed_precision = '-mixed' in use_mixed_precision self.model = LightningModelWrapper( self.model, criterion, + use_mixed_precision=use_mixed_precision, datamodule=getattr(self, "datamodule", None), metrics=getattr(self, "metrics", None), - optimizer_config=self.opt["optimizer"] if is_train else None, - scheduler_config=self.opt["scheduler"] if is_train else None, + optimizer_config=self.opt.get("optimizer", None) if is_train else None, + scheduler_config=self.opt.get("scheduler", None) if is_train else None, scheduler_kwargs={ "num_epochs": num_epochs, "num_iterations": num_epochs * len(self.train_dataloader), @@ -259,21 +311,25 @@ def init_model_with_loss(self, is_train=True): pretrained = self.opt["global"].get("pretrained", None) if pretrained: - state_dict = torch.load(pretrained, map_location="cpu") + state_dict = torch.load(pretrained, map_location="cpu", weights_only=False) try: self.model.load_state_dict(state_dict["state_dict"], strict=False) - self.logger.text( + self._log( f"Loaded pretrained model from {pretrained}", level=LoggerObserver.SUCCESS, ) except Exception as e: - self.logger.text( + self._log( f"Loaded pretrained model from {pretrained}. Mismatched keys: {e}", level=LoggerObserver.WARN, ) - def init_metrics(self): + def init_metrics(self) -> None: CLASSNAMES = self.classnames + if self.opt["metrics"] is None: + self.metrics = None + return + self.metrics = get_instance_recursively( self.opt["metrics"], registry=self.metric_registry, @@ -281,7 +337,7 @@ def init_metrics(self): classnames=CLASSNAMES, ) - def init_callbacks(self): + def init_callbacks(self) -> list[Any]: callbacks = get_instance_recursively( self.opt["callbacks"], save_dir=getattr(self, "savedir", "runs"), @@ -291,42 +347,34 @@ def init_callbacks(self): ) return callbacks - def init_trainer(self, callbacks): + def init_trainer(self, callbacks: list[Any]) -> None: + # Check if we already have a ModelSummary callback to avoid redundancy warning + has_summary = any("ModelSummary" in str(type(c)) for c in callbacks) + self.trainer = get_instance( self.opt["trainer"], default_root_dir=getattr(self, "savedir", "runs"), deterministic="warn", callbacks=callbacks, + enable_model_summary=not has_summary, registry=self.trainer_registry, ) - def save_configs(self): + def save_configs(self) -> None: with open(os.path.join(self.savedir, "pipeline.yaml"), "w") as f: OmegaConf.save(config=self.opt, f=f) - def init_registry(self): - self.model_registry = MODEL_REGISTRY - self.dataset_registry = DATASET_REGISTRY - self.dataloader_registry = DATALOADER_REGISTRY - self.metric_registry = METRIC_REGISTRY - self.loss_registry = LOSS_REGISTRY - self.callbacks_registry = CALLBACKS_REGISTRY - self.trainer_registry = TRAINER_REGISTRY - self.transform_registry = TRANSFORM_REGISTRY - self.logger.text( - "You should override the init_registry() function", - LoggerObserver.CRITICAL, - ) - - def init_pipeline(self, phase: str = "train"): + def init_pipeline(self, phase: str = "train") -> None: if self.initialized: return self.init_globals() self.init_registry() + if phase == "train": self.init_train_dataloader() self.init_validation_dataloader() - self.init_test_dataloader() + if "test" in self.opt["data"]["dataset"]: + self.init_test_dataloader() self.init_datamodule() self.init_metrics() self.init_model_with_loss() @@ -339,31 +387,28 @@ def init_pipeline(self, phase: str = "train"): self.init_validation_dataloader() self.init_datamodule() self.init_metrics() - self.init_model_with_loss(is_train=(phase == "train")) + self.init_model_with_loss(is_train=False) callbacks = [] + # Always add core callbacks if getattr(self.model, "metrics", None): callbacks.insert( 0, - self.callbacks_registry.get("MetricLoggerCallback")( - save_dir=self.savedir - ), - ) - if getattr(self.model, "criterion", None): - callbacks.insert( - 0, - self.callbacks_registry.get("LossLoggerCallback")( - print_interval=self.opt["trainer"]["args"].get( - "log_every_n_steps", None - ), - ), + self.callbacks_registry.get("MetricLoggerCallback")(save_dir=self.savedir), ) + callbacks.insert( + 0, + self.callbacks_registry.get("LossLoggerCallback")( + print_interval=self.opt["trainer"]["args"].get("log_every_n_steps", None), + ), + ) callbacks.insert(0, self.callbacks_registry.get("TimerCallback")()) self.init_trainer(callbacks) self.initialized = True - def fit(self): + def fit(self) -> None: + """Run the full training pipeline.""" self.init_pipeline(phase="train") self.trainer.fit( model=self.model, @@ -371,114 +416,102 @@ def fit(self): ckpt_path=self.resume, ) - def evaluate(self): + def evaluate(self) -> dict[str, Any]: + """Run validation and return metrics.""" self.init_pipeline(phase="validation") self.trainer.validate( model=self.model, datamodule=self.datamodule, ckpt_path=self.resume, ) - return self.trainer.callback_metrics - def test(self): + def test(self) -> dict[str, Any]: + """Run testing and return metrics.""" self.init_pipeline(phase="test") self.trainer.test( model=self.model, datamodule=self.datamodule, ckpt_path=self.resume, ) - return self.trainer.callback_metrics -class BaseTestPipeline(object): - def __init__(self, opt: DictConfig): +class BaseTestPipeline(_PipelineBase): + """ + Lightweight pipeline for inference/testing only. + Shares globals/registry init logic with ``BasePipeline`` via ``_PipelineBase``. + """ - super(BaseTestPipeline, self).__init__() - self.opt = opt - self.seed = self.opt["global"].get("seed", 1702) - seed_everything(self.seed) + def __init__(self, opt: DictConfig) -> None: + super().__init__(opt) + + def init_globals(self) -> None: + """Initialize globals without image writer (not needed for inference).""" + # Set float32 matmul precision for Tensor Cores (e.g., A100) + torch.set_float32_matmul_precision("medium") - def init_globals(self): - # Main Loggers self.logger = LoggerObserver.getLogger("main") - # Global variables self.exp_name = self.opt["global"].get("exp_name", None) self.exist_ok = self.opt["global"].get("exist_ok", False) self.debug = self.opt["global"].get("debug", False) self.transform_cfg = self.opt["global"].get("cfg_transform", None) - # Experiment name - if self.exp_name: - self.savedir = os.path.join( - self.opt["global"].get("save_dir", "runs"), self.exp_name - ) - if not self.exist_ok: - self.savedir = get_new_folder_name(self.savedir) - else: - self.savedir = os.path.join( - self.opt["global"].get("save_dir", "runs"), - datetime.now().strftime("%Y-%m-%d_%H-%M-%S"), - ) - os.makedirs(self.savedir, exist_ok=True) - + self.savedir = self._setup_savedir() self.transform_cfg = self.opt.get("augmentations", None) - # Logging to files file_logger = FileLogger(__name__, self.savedir, debug=self.debug) self.logger.subscribe(file_logger) - self.logger.text(self.opt, level=LoggerObserver.INFO) - self.logger.text( - f"Everything will be saved to {self.savedir}", - level=LoggerObserver.INFO, - ) + self._log(str(self.opt)) + self._log(f"Everything will be saved to {self.savedir}") - def init_registry(self): + def init_registry(self) -> None: self.model_registry = MODEL_REGISTRY self.dataset_registry = DATASET_REGISTRY self.dataloader_registry = DATALOADER_REGISTRY self.transform_registry = TRANSFORM_REGISTRY - self.logger.text( - "You should override the init_registry() function", - LoggerObserver.INFO, - ) + self._log("You should override the init_registry() function") - def init_test_dataloader(self): - # Transforms & Datasets + def init_test_dataloader(self) -> None: self.transform = get_instance_recursively( self.transform_cfg, registry=self.transform_registry ) transform_cfg = ( - self.transform["test"] - if "test" in self.transform - else self.transform["val"] + self.transform.get("test") if isinstance(self.transform, dict) else self.transform ) + if transform_cfg is None: + transform_cfg = ( + self.transform.get("val") if isinstance(self.transform, dict) else self.transform + ) + + # Handle both nested (data.dataset.test) and flat (data.dataset) configs + test_data_cfg = self.opt["data"]["dataset"] + if "name" not in test_data_cfg: + test_data_cfg = test_data_cfg.get("test") or test_data_cfg.get("val") self.dataset = get_instance( - self.opt["data"]["dataset"].get("test", "val"), + test_data_cfg, registry=DATASET_REGISTRY, transform=transform_cfg, ) + # Handle both nested (data.dataloader.test) and flat (data.dataloader) configs + test_loader_cfg = self.opt["data"]["dataloader"] + if "name" not in test_loader_cfg: + test_loader_cfg = test_loader_cfg.get("test") or test_loader_cfg.get("val") + self.dataloader = get_instance( - self.opt["data"]["dataloader"].get("test", "val"), + test_loader_cfg, registry=DATALOADER_REGISTRY, dataset=self.dataset, ) - self.logger.text( - f"Number of test samples: {len(self.dataset)}", - level=LoggerObserver.INFO, - ) - self.logger.text( - f"Number of test iterations each epoch: {len(self.dataloader)}", - level=LoggerObserver.INFO, - ) + self._log(f"Number of test samples: {len(self.dataset)}") + self._log(f"Number of test iterations each epoch: {len(self.dataloader)}") - def init_model(self): + def init_model(self) -> None: CLASSNAMES = getattr(self.dataset, "classnames", None) self.model = get_instance( self.opt["model"], @@ -489,18 +522,18 @@ def init_model(self): self.model = LightningModelWrapper(self.model) self.model.eval() - def init_loading(self): + def init_loading(self) -> None: self.weights = self.opt["global"].get("pretrained", None) if self.weights: - state_dict = torch.load(self.weights, map_location="cpu") - self.model.load_state_dict(state_dict["state_dict"]) + state_dict = torch.load(self.weights, map_location="cpu", weights_only=False) + self.model.load_state_dict(state_dict["state_dict"], strict=False) - def init_pipeline(self): + def init_pipeline(self) -> None: self.init_globals() self.init_registry() self.init_test_dataloader() self.init_model() self.init_loading() - def inference(self): + def inference(self) -> Any: raise NotImplementedError() diff --git a/theseus/base/trainer/__init__.py b/theseus/base/trainer/__init__.py index 6bc88e12..25318e64 100644 --- a/theseus/base/trainer/__init__.py +++ b/theseus/base/trainer/__init__.py @@ -1,3 +1,7 @@ +from __future__ import annotations + +from typing import Any + from lightning.pytorch.strategies import DDPStrategy from lightning.pytorch.trainer import Trainer as plTrainer @@ -7,19 +11,36 @@ class Trainer(plTrainer): - def __init__(self, *args, **kwargs): - if "use_nccl_strategy" in kwargs.keys() and "strategy" not in kwargs.keys(): - # Default to DDP strategy with NCCL backend - use_nccl_strategy = kwargs.pop("use_nccl_strategy") - if use_nccl_strategy: - ddp = DDPStrategy( - process_group_backend="nccl", find_unused_parameters=True + """ + Extended Lightning Trainer with convenience options for distributed strategies. + + Supports ``use_nccl_strategy`` for auto-configuring DDP with NCCL backend, + and ``use_fsdp_strategy`` for Fully Sharded Data Parallel. + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + # Handle NCCL DDP strategy shortcut + use_nccl = kwargs.pop("use_nccl_strategy", False) + use_fsdp = kwargs.pop("use_fsdp_strategy", False) + + if "strategy" not in kwargs: + if use_nccl: + kwargs["strategy"] = DDPStrategy( + process_group_backend="nccl", + find_unused_parameters=True, ) - super().__init__(strategy=ddp, *args, **kwargs) - else: - super().__init__(*args, **kwargs) - else: - super().__init__(*args, **kwargs) + elif use_fsdp: + try: + from lightning.pytorch.strategies import FSDPStrategy + + kwargs["strategy"] = FSDPStrategy() + except ImportError: + raise ImportError( + "FSDP strategy requires lightning >= 2.0. " + "Please upgrade your lightning installation." + ) + + super().__init__(*args, **kwargs) TRAINER_REGISTRY.register(Trainer, prefix="pl") diff --git a/theseus/base/utilities/cuda.py b/theseus/base/utilities/cuda.py index e875ac5c..8daf8046 100644 --- a/theseus/base/utilities/cuda.py +++ b/theseus/base/utilities/cuda.py @@ -1,6 +1,7 @@ -""" CUDA / AMP utils +"""CUDA / AMP utils Hacked together by / Copyright 2020 Ross Wightman """ + from typing import Any import torch @@ -21,20 +22,19 @@ def get_devices_info(device_names="0"): for i, device_id in enumerate(device_names.split(",")): p = torch.cuda.get_device_properties(i) devices_info.append( - f"CUDA:{device_id} ({p.name}, {p.total_memory / 1024 ** 2}MB)" + f"CUDA:{device_id} ({p.name}, {p.total_memory / 1024**2}MB)" ) # bytes to MB devices_info = "\n".join(devices_info) return devices_info def get_device(name="cpu") -> torch.device: - if name.startswith("cuda"): - if not torch.cuda.is_available(): - LOGGER.text( - "CUDA is not available. Using CPU...", - level=LoggerObserver.WARN, - ) - name = "cpu" + if name.startswith("cuda") and not torch.cuda.is_available(): + LOGGER.text( + "CUDA is not available. Using CPU...", + level=LoggerObserver.WARN, + ) + name = "cpu" return torch.device(name) diff --git a/theseus/base/utilities/download.py b/theseus/base/utilities/download.py index e05c4c05..39adee21 100644 --- a/theseus/base/utilities/download.py +++ b/theseus/base/utilities/download.py @@ -14,7 +14,7 @@ def download_from_drive(id_or_url, output, md5=None, quiet=False, cache=True): if id_or_url.startswith("http") or id_or_url.startswith("https"): url = id_or_url else: - url = "https://drive.google.com/uc?id={}".format(id_or_url) + url = f"https://drive.google.com/uc?id={id_or_url}" if not cache: return gdown.download(url, output, quiet=quiet) @@ -46,14 +46,11 @@ def download_from_url(url, root=None, filename=None): try: LOGGER.text("Downloading " + url + " to " + fpath, level=LoggerObserver.DEBUG) urlreq.urlretrieve(url, fpath) - except (urlreq.error.URLError, IOError) as e: + except (OSError, urlreq.error.URLError): if url[:5] == "https": url = url.replace("https:", "http:") LOGGER.text( - "Failed download. Trying https -> http instead.Downloading " - + url - + " to " - + fpath, + "Failed download. Trying https -> http instead.Downloading " + url + " to " + fpath, level=LoggerObserver.DEBUG, ) urlreq.urlretrieve(url, fpath) @@ -61,18 +58,14 @@ def download_from_url(url, root=None, filename=None): return fpath -def download_from_wandb( - filename, run_path, save_dir, rename=None, generate_id_text_file=False -): +def download_from_wandb(filename, run_path, save_dir, rename=None, generate_id_text_file=False): import wandb try: path = wandb.restore(filename, run_path=run_path, root=save_dir) LOGGER.text( - "Successfully download {} from wandb run path {}".format( - filename, run_path - ), + f"Successfully download {filename} from wandb run path {run_path}", level=LoggerObserver.INFO, ) @@ -85,11 +78,11 @@ def download_from_wandb( if rename: new_name = str(Path(path.name).resolve().parent / rename) os.rename(Path(path.name).resolve(), new_name) - LOGGER.text("Saved to {}".format(new_name), level=LoggerObserver.INFO) + LOGGER.text(f"Saved to {new_name}", level=LoggerObserver.INFO) return new_name LOGGER.text( - "Saved to {}".format((Path(save_dir) / path.name).resolve()), + f"Saved to {(Path(save_dir) / path.name).resolve()}", level=LoggerObserver.INFO, ) return path.name diff --git a/theseus/base/utilities/getter.py b/theseus/base/utilities/getter.py index ab76e7ae..f1c99c1d 100644 --- a/theseus/base/utilities/getter.py +++ b/theseus/base/utilities/getter.py @@ -1,63 +1,101 @@ +from __future__ import annotations + +import functools import inspect +from typing import Any from omegaconf import DictConfig, ListConfig from theseus.registry import Registry -def get_instance_with_kwargs(registry, name, args: list = None, kwargs: dict = {}): - # get keyword arguments from class signature - inspection = inspect.signature(registry.get(name)) +# Cache for inspect.signature to avoid repeated introspection +@functools.lru_cache(maxsize=256) +def _cached_signature(cls: type) -> inspect.Signature: + """Cache inspect.signature results for performance.""" + return inspect.signature(cls) + + +def get_instance_with_kwargs( + registry: Registry, + name: str, + args: Any = None, + kwargs: dict[str, Any] | None = None, +) -> Any: + """ + Instantiate a class from registry by name, filtering kwargs to match + the class constructor signature. + """ + if kwargs is None: + kwargs = {} + + cls = registry.get(name) + inspection = _cached_signature(cls) class_kwargs = inspection.parameters.keys() if isinstance(args, (dict, DictConfig)): - # override kwargs (from parent) with args (from config) + # Override kwargs (from parent) with args (from config) kwargs.update(args) args = None if "kwargs" in class_kwargs: if args is None: - return registry.get(name)(**kwargs) + return cls(**kwargs) else: - return registry.get(name)(*args, **kwargs) + return cls(*args, **kwargs) else: filtered_kwargs = {k: v for k, v in kwargs.items() if k in class_kwargs} if args is None: - return registry.get(name)(**filtered_kwargs) + return cls(**filtered_kwargs) else: - return registry.get(name)(*args, **filtered_kwargs) + return cls(*args, **filtered_kwargs) -def get_instance(config, registry: Registry, **kwargs): - # ref https://github.com/vltanh/torchan/blob/master/torchan/utils/getter.py - assert "name" in config - args = config.get("args", []) +def get_instance(config: DictConfig | dict, registry: Registry, **kwargs: Any) -> Any: + """ + Instantiate a single class from config dict with 'name' and optional 'args'. + + Args: + config: Dict-like with 'name' key and optional 'args' key. + registry: Registry to look up the class by name. + **kwargs: Additional keyword arguments passed to the constructor. + Returns: + Instantiated object. + """ + assert "name" in config, f"Config must contain 'name' key, got: {list(config.keys())}" + args = config.get("args", []) return get_instance_with_kwargs(registry, config["name"], args, kwargs) -def get_instance_recursively(config, registry: Registry, **kwargs): +def get_instance_recursively( + config: Any, + registry: Registry, + **kwargs: Any, +) -> Any: + """ + Recursively walk a config tree and instantiate all objects that have + a 'name' key, using the given registry. + + Supports nested lists, dicts, and DictConfig/ListConfig. + """ if isinstance(config, (list, tuple, ListConfig)): - out = [ - get_instance_recursively(item, registry=registry, **kwargs) - for item in config - ] - return out + return [get_instance_recursively(item, registry=registry, **kwargs) for item in config] + if isinstance(config, (dict, DictConfig)): - if "name" in config.keys(): + if "name" in config: if registry: - args = get_instance_recursively( - config.get("args", {}), registry, **kwargs - ) + args = get_instance_recursively(config.get("args", {}), registry, **kwargs) return get_instance_with_kwargs(registry, config["name"], args, kwargs) - else: - out = {} - for k, v in config.items(): - out[k] = get_instance_recursively(v, registry=registry, **kwargs) - return out + return { + k: get_instance_recursively(v, registry=registry, **kwargs) + for k, v in config.items() + } + return config -def get_function(name): +def get_function(name: str) -> Any: + """Get a function by name from the global scope.""" return globals()[name] diff --git a/theseus/base/utilities/hub.py b/theseus/base/utilities/hub.py new file mode 100644 index 00000000..625ce677 --- /dev/null +++ b/theseus/base/utilities/hub.py @@ -0,0 +1,243 @@ +""" +HuggingFace Hub integration for Theseus models. + +Provides ``HuggingFaceHubMixin`` for save/load/push operations, and +``HuggingFaceHubCallback`` for automatic checkpoint pushing during training. +""" + +from __future__ import annotations + +import json +import logging +import os +from pathlib import Path +from typing import Any + +import torch + +logger = logging.getLogger(__name__) + +# Optional imports — graceful degradation if not installed +try: + from safetensors.torch import load_file, save_file + + _SAFETENSORS_AVAILABLE = True +except ImportError: + _SAFETENSORS_AVAILABLE = False + +try: + from huggingface_hub import HfApi, ModelCard, ModelCardData + + _HF_HUB_AVAILABLE = True +except ImportError: + _HF_HUB_AVAILABLE = False + + +def _check_hf_hub() -> None: + if not _HF_HUB_AVAILABLE: + raise ImportError( + "huggingface-hub is required for HuggingFace Hub integration. " + "Install it with: pip install huggingface-hub" + ) + + +class HuggingFaceHubMixin: + """ + Mixin that adds ``save_pretrained()``, ``from_pretrained()``, and + ``push_to_hub()`` to any ``nn.Module`` subclass. + + Supports both ``safetensors`` (preferred) and PyTorch ``.bin`` formats. + + Example:: + + class MyModel(nn.Module, HuggingFaceHubMixin): + def __init__(self, config): + super().__init__() + self.config = config + self.linear = nn.Linear(10, 10) + + model = MyModel(config={"hidden_size": 10}) + model.save_pretrained("./my-model") + model.push_to_hub("username/my-model") + + loaded = MyModel.from_pretrained("username/my-model", config={"hidden_size": 10}) + """ + + def save_pretrained( + self, + save_directory: str | Path, + *, + config: dict[str, Any] | None = None, + use_safetensors: bool = True, + ) -> None: + """ + Save model weights and config to a directory. + + Args: + save_directory: Path to save the model. + config: Optional config dict to save alongside weights. + use_safetensors: Use safetensors format if available (default True). + """ + save_directory = Path(save_directory) + save_directory.mkdir(parents=True, exist_ok=True) + + # Save weights + if use_safetensors and _SAFETENSORS_AVAILABLE: + weights_path = save_directory / "model.safetensors" + save_file(self.state_dict(), str(weights_path)) # type: ignore[arg-type] + else: + weights_path = save_directory / "pytorch_model.bin" + torch.save(self.state_dict(), weights_path) # type: ignore[arg-type] + + # Save config + if config is None: + config = getattr(self, "config", {}) + if config: + config_path = save_directory / "config.json" + with open(config_path, "w") as f: + json.dump(config, f, indent=2, default=str) + + logger.info(f"Model saved to {save_directory}") + + @classmethod + def from_pretrained( + cls, + pretrained_path: str | Path, + *, + use_safetensors: bool = True, + map_location: str = "cpu", + **kwargs: Any, + ) -> HuggingFaceHubMixin: + """ + Load a model from a local directory or HuggingFace Hub repo. + + Args: + pretrained_path: Local path or HuggingFace repo ID. + use_safetensors: Prefer safetensors format. + map_location: Device to map weights to. + **kwargs: Passed to the model constructor. + """ + pretrained_path = Path(pretrained_path) + + # Load config if available + config_path = pretrained_path / "config.json" + config = {} + if config_path.exists(): + with open(config_path) as f: + config = json.load(f) + + # Merge config with kwargs (kwargs take precedence) + merged_kwargs = {**config, **kwargs} + model = cls(**merged_kwargs) + + # Load weights + safetensors_path = pretrained_path / "model.safetensors" + bin_path = pretrained_path / "pytorch_model.bin" + + if use_safetensors and _SAFETENSORS_AVAILABLE and safetensors_path.exists(): + state_dict = load_file(str(safetensors_path), device=map_location) + elif bin_path.exists(): + state_dict = torch.load(bin_path, map_location=map_location, weights_only=True) + else: + raise FileNotFoundError( + f"No model weights found in {pretrained_path}. " + f"Expected 'model.safetensors' or 'pytorch_model.bin'." + ) + + model.load_state_dict(state_dict, strict=False) # type: ignore[arg-type] + logger.info(f"Model loaded from {pretrained_path}") + return model # type: ignore[return-value] + + def push_to_hub( + self, + repo_id: str, + *, + commit_message: str = "Upload model", + private: bool = False, + token: str | None = None, + config: dict[str, Any] | None = None, + model_card: str | None = None, + use_safetensors: bool = True, + ) -> str: + """ + Push model to HuggingFace Hub. + + Args: + repo_id: HuggingFace repo ID (e.g. ``username/model-name``). + commit_message: Commit message for the push. + private: Whether the repo should be private. + token: HuggingFace API token. Uses cached token if None. + config: Optional config dict. + model_card: Optional model card text. + use_safetensors: Use safetensors format. + + Returns: + URL of the pushed model on HuggingFace Hub. + """ + _check_hf_hub() + + import tempfile + + api = HfApi(token=token) + + # Create repo if it doesn't exist + api.create_repo(repo_id=repo_id, private=private, exist_ok=True) + + with tempfile.TemporaryDirectory() as tmpdir: + self.save_pretrained(tmpdir, config=config, use_safetensors=use_safetensors) + + # Generate model card if not provided + if model_card is None: + model_card = self._generate_model_card(repo_id, config) + + card_path = os.path.join(tmpdir, "README.md") + with open(card_path, "w") as f: + f.write(model_card) + + url = api.upload_folder( + repo_id=repo_id, + folder_path=tmpdir, + commit_message=commit_message, + ) + + logger.info(f"Model pushed to https://huggingface.co/{repo_id}") + return url + + def _generate_model_card( + self, + repo_id: str, + config: dict[str, Any] | None = None, + ) -> str: + """Generate a basic model card.""" + model_name = repo_id.split("/")[-1] if "/" in repo_id else repo_id + trainable_params = sum( + p.numel() + for p in self.parameters() + if p.requires_grad # type: ignore[union-attr] + ) + total_params = sum(p.numel() for p in self.parameters()) # type: ignore[union-attr] + + card = f"""--- +library_name: theseus +tags: +- pytorch +- theseus +--- + +# {model_name} + +This model was trained using the [Theseus](https://github.com/kaylode/theseus) framework. + +## Model Details + +- **Framework**: Theseus v2.0 (PyTorch Lightning) +- **Total Parameters**: {total_params:,} +- **Trainable Parameters**: {trainable_params:,} +""" + + if config: + card += "\n## Configuration\n\n```json\n" + card += json.dumps(config, indent=2, default=str) + card += "\n```\n" + + return card diff --git a/theseus/base/utilities/loading.py b/theseus/base/utilities/loading.py index a6741d28..ca388a7d 100644 --- a/theseus/base/utilities/loading.py +++ b/theseus/base/utilities/loading.py @@ -12,7 +12,7 @@ def load_yaml(path): - with open(path, "rt") as f: + with open(path) as f: return yaml.safe_load(f) @@ -61,7 +61,7 @@ def load_state_dict(instance, state_dict, key=None, strict=True): ) return instance else: - if key in state_dict.keys(): + if key in state_dict: return state_dict[key] else: LOGGER.text(f"Cannot load key={key} from state_dict", LoggerObserver.WARN) diff --git a/theseus/base/utilities/loggers/image_writer.py b/theseus/base/utilities/loggers/image_writer.py index 92bcea66..8bc6ad13 100644 --- a/theseus/base/utilities/loggers/image_writer.py +++ b/theseus/base/utilities/loggers/image_writer.py @@ -1,7 +1,6 @@ import os import matplotlib as mpl -import matplotlib.pyplot as plt import plotly.graph_objs as go from .observer import LoggerObserver diff --git a/theseus/base/utilities/loggers/observer.py b/theseus/base/utilities/loggers/observer.py index ac08bab7..af9f5426 100644 --- a/theseus/base/utilities/loggers/observer.py +++ b/theseus/base/utilities/loggers/observer.py @@ -1,47 +1,66 @@ import logging import os - -import matplotlib as mpl -import matplotlib.pyplot as plt -import numpy as np -import plotly.graph_objs as go -import torch - -mpl.use("Agg") - +import sys import threading from inspect import getframeinfo, stack -from typing import Dict, List +from typing import Any +import numpy as np from tabulate import tabulate from .subscriber import LoggerSubscriber -def get_type(value): - if isinstance(value, torch.nn.Module): - return LoggerObserver.TORCH_MODULE - if isinstance(value, mpl.figure.Figure) or isinstance(value, go.Figure): - return LoggerObserver.FIGURE - if isinstance(value, torch.Tensor) or isinstance(value, np.ndarray): - if len(value.shape) == 2: +def get_type(value: Any) -> str: + """Infer the log type from a value.""" + if "torch" in sys.modules: + import torch + + if isinstance(value, torch.nn.Module): + return LoggerObserver.TORCH_MODULE + if isinstance(value, torch.Tensor) and len(value.shape) == 2: return LoggerObserver.EMBED - if isinstance(value, (int, float)): + + if "matplotlib" in sys.modules: + import matplotlib as mpl + + if isinstance(value, mpl.figure.Figure): + return LoggerObserver.FIGURE + + if "plotly" in sys.modules: + import plotly.graph_objs as go + + if isinstance(value, go.Figure): + return LoggerObserver.FIGURE + + if isinstance(value, np.ndarray) and len(value.shape) == 2: + return LoggerObserver.EMBED + + if isinstance(value, (int, float, np.number)): return LoggerObserver.SCALAR if isinstance(value, str): if value.endswith(".html"): return LoggerObserver.HTML else: return LoggerObserver.TEXT - else: - raise ValueError(f"Fail to log undefined type: {type(value)}") + raise ValueError(f"Fail to log undefined type: {type(value)}") -class LoggerObserver(object): - """Logger Oberserver Degisn Pattern - notifies every subscribers when .log() is called +class LoggerObserver: """ + Logger Observer Design Pattern. + + Notifies every subscriber when ``.log()`` is called. + Uses a dispatch table instead of if-chains for O(1) routing. + Example:: + + logger = LoggerObserver.getLogger("main") + logger.text("Hello world", level=LoggerObserver.INFO) + logger.log([{"tag": "loss", "value": 0.5, "type": "scalar"}]) + """ + + # Log type constants SCALAR = "scalar" FIGURE = "figure" TORCH_MODULE = "torch_module" @@ -52,6 +71,7 @@ class LoggerObserver(object): VIDEO = "video" HTML = "html" + # Log level constants WARN = logging.WARN ERROR = logging.ERROR DEBUG = logging.DEBUG @@ -59,20 +79,40 @@ class LoggerObserver(object): CRITICAL = logging.CRITICAL SUCCESS = "SUCCESS" - instances = {} + # Singleton instances + instances: dict[str, "LoggerObserver"] = {} _lock = threading.Lock() - def __new__(cls, name=None, *args, **kwargs): + # Dispatch table: maps log type -> subscriber method name + _DISPATCH: dict[str, str] = { + SCALAR: "log_scalar", + FIGURE: "log_figure", + TORCH_MODULE: "log_torch_module", + TEXT: "log_text", + SPECIAL_TEXT: "log_spec_text", + EMBED: "log_embedding", + TABLE: "log_table", + VIDEO: "log_video", + HTML: "log_html", + } + + def __new__(cls, name: str | None = None, *args: Any, **kwargs: Any) -> "LoggerObserver": + if name is None: + name = str(os.getpid()) with cls._lock: - if name is None: - name = str(os.getpid()) - if name in LoggerObserver.instances.keys(): - return LoggerObserver.instances[name] - - return object.__new__(cls, *args, **kwargs) - - def __init__(self, name) -> None: - self.subscriber = [] + if name not in cls.instances: + instance = object.__new__(cls) + instance._initialized = False + cls.instances[name] = instance + return cls.instances[name] + + def __init__(self, name: str | None = None) -> None: + if self._initialized: + return + self._initialized = True + self.subscriber: list[LoggerSubscriber] = [] + if name is None: + name = str(os.getpid()) self.name = name # Init with a stdout logger @@ -81,63 +121,52 @@ def __init__(self, name) -> None: logger = StdoutLogger(name=self.name, debug=True) self.subscribe(logger) - LoggerObserver.instances[name] = self - - def __del__(self): - for subcriber in self.subscriber: - del subcriber - if self.name in LoggerObserver.instances.keys(): - LoggerObserver.instances.pop(self.name) + def __del__(self) -> None: + for subscriber in self.subscriber: + del subscriber + if self.name in LoggerObserver.instances: + LoggerObserver.instances.pop(self.name, None) @classmethod - def getLogger(cls, name): - if name in LoggerObserver.instances.keys(): - return LoggerObserver.instances[name] - + def getLogger(cls, name: str) -> "LoggerObserver": + """Get or create a logger by name.""" return cls(name) - def subscribe(self, subscriber: LoggerSubscriber): + def subscribe(self, subscriber: LoggerSubscriber) -> None: + """Add a subscriber that will receive log events.""" self.subscriber.append(subscriber) - def log(self, logs: List[Dict]): - for subscriber in self.subscriber: - for log in logs: - tag = log["tag"] - value = log["value"] - log_type = log["type"] if "type" in log.keys() else get_type(value) - kwargs = log["kwargs"] if "kwargs" in log.keys() else {} - - if log_type == LoggerObserver.SCALAR: - subscriber.log_scalar(tag=tag, value=value, **kwargs) - - if log_type == LoggerObserver.FIGURE: - subscriber.log_figure(tag=tag, value=value, **kwargs) - - if log_type == LoggerObserver.TORCH_MODULE: - subscriber.log_torch_module(tag=tag, value=value, **kwargs) - - if log_type == LoggerObserver.TEXT: - subscriber.log_text(tag=tag, value=value, **kwargs) - - if log_type == LoggerObserver.EMBED: - subscriber.log_embedding(tag=tag, value=value, **kwargs) - - if log_type == LoggerObserver.SPECIAL_TEXT: - subscriber.log_spec_text(tag=tag, value=value, **kwargs) - - if log_type == LoggerObserver.TABLE: - subscriber.log_table(tag=tag, value=value, **kwargs) - - if log_type == LoggerObserver.VIDEO: - subscriber.log_video(tag=tag, value=value, **kwargs) - - if log_type == LoggerObserver.HTML: - subscriber.log_html(tag=tag, value=value, **kwargs) - - def text(self, value, level=logging.INFO): + def log(self, logs: list[dict[str, Any]]) -> None: """ - Text logging + Dispatch log entries to all subscribers using the dispatch table. + Each log entry must have 'tag' and 'value' keys, with optional 'type' and 'kwargs'. """ + # Support distributed logging + is_master = True + if "torch" in sys.modules: + import torch + + if torch.distributed.is_initialized() and torch.distributed.get_rank() != 0: + is_master = False + if not is_master: + return + + for subscriber in self.subscriber: + for entry in logs: + tag = entry["tag"] + value = entry["value"] + log_type = entry.get("type", get_type(value)) + kwargs = entry.get("kwargs", {}) + + # Use dispatch table for O(1) routing + method_name = self._DISPATCH.get(log_type) + if method_name is not None: + method = getattr(subscriber, method_name, None) + if method is not None: + method(tag=tag, value=value, **kwargs) + + def text(self, *value: Any, level: int = logging.INFO) -> None: + """Convenience method for text logging with source location.""" caller = getframeinfo(stack()[1][0]) function_name = stack()[1][3] filename = "//".join(caller.filename.split("theseus")[1:])[ @@ -145,11 +174,21 @@ def text(self, value, level=logging.INFO): ] # split filename based on project name lineno = caller.lineno + texts = [] + for v in value: + if isinstance(v, dict): + import json + + texts.append(json.dumps(v, indent=4, default=str)) + else: + texts.append(str(v)) + value_str = " ".join(texts) + self.log( [ { "tag": "stdout", - "value": value, + "value": value_str, "type": LoggerObserver.TEXT, "kwargs": { "level": level, diff --git a/theseus/base/utilities/loggers/stdout_logger.py b/theseus/base/utilities/loggers/stdout_logger.py index 064d50a7..9d883a46 100644 --- a/theseus/base/utilities/loggers/stdout_logger.py +++ b/theseus/base/utilities/loggers/stdout_logger.py @@ -1,5 +1,5 @@ import sys - +import os from loguru import logger from .observer import LoggerObserver, LoggerSubscriber @@ -53,6 +53,7 @@ class FileLogger(BaseTextLogger): def __init__(self, name, logdir, rotation="10 MB", debug=False): self.logdir = logdir + os.makedirs(self.logdir, exist_ok=True) self.filename = f"{self.logdir}/log.txt" super().__init__(name) @@ -65,15 +66,17 @@ def __init__(self, name, logdir, rotation="10 MB", debug=False): self.filename, rotation=rotation, level=level, - filter=lambda record: "filelog" in record["extra"], + filter=lambda record: ( + record["extra"].get("filelog") and record["extra"].get("name") == self.name + ), ) def log_text(self, tag, value, level=LoggerObserver.DEBUG, **kwargs): - filename = kwargs.get("filename", None) - funcname = kwargs.get("funcname", None) - lineno = kwargs.get("lineno", None) + filename = kwargs.get("filename") + funcname = kwargs.get("funcname") + lineno = kwargs.get("lineno") with logger.contextualize( - filelog=True, filename=filename, funcname=funcname, lineno=lineno + filelog=True, name=self.name, filename=filename, funcname=funcname, lineno=lineno ): return super().log_text(tag, value, level, **kwargs) @@ -103,14 +106,16 @@ def __init__(self, name, debug=False): diagnose=True, level=level, format=self.message_format, - filter=lambda record: "stdout" in record["extra"], + filter=lambda record: ( + record["extra"].get("stdout") and record["extra"].get("name") == self.name + ), ) def log_text(self, tag, value, level=LoggerObserver.DEBUG, **kwargs): - filename = kwargs.get("filename", None) - funcname = kwargs.get("funcname", None) - lineno = kwargs.get("lineno", None) + filename = kwargs.get("filename") + funcname = kwargs.get("funcname") + lineno = kwargs.get("lineno") with logger.contextualize( - stdout=True, filename=filename, funcname=funcname, lineno=lineno + stdout=True, name=self.name, filename=filename, funcname=funcname, lineno=lineno ): return super().log_text(tag, value, level, **kwargs) diff --git a/theseus/base/utilities/loggers/tsb_logger.py b/theseus/base/utilities/loggers/tsb_logger.py index e4f9f53a..57d7aec2 100644 --- a/theseus/base/utilities/loggers/tsb_logger.py +++ b/theseus/base/utilities/loggers/tsb_logger.py @@ -7,9 +7,11 @@ import pandas as pd import torch from PIL import Image -from tensorboard.backend.event_processing.event_accumulator import EventAccumulator try: + from tensorboard.backend.event_processing.event_accumulator import EventAccumulator + from torch.utils.tensorboard import SummaryWriter + from torchvision.transforms import ToTensor import tensorboard as tb import tensorflow as tf @@ -17,8 +19,6 @@ except: pass -from torch.utils.tensorboard import SummaryWriter -from torchvision.transforms import ToTensor from .observer import LoggerObserver, LoggerSubscriber @@ -71,14 +71,7 @@ def log_torch_module(self, tag, value, inputs, **kwargs): self.writer.add_graph(value, inputs) def log_embedding( - self, - tag, - value, - label_img=None, - step=0, - metadata=None, - metadata_header=None, - **kwargs + self, tag, value, label_img=None, step=0, metadata=None, metadata_header=None, **kwargs ): """ Write a embedding projection to tensorboard @@ -140,7 +133,6 @@ def tflog2pandas(path: str) -> pd.DataFrame: runlog_data = pd.DataFrame({"metric": [], "value": [], "step": []}) runfig_data = pd.DataFrame({"name": [], "value": [], "step": []}) try: - ## Scalar values event_acc = EventAccumulator(path, DEFAULT_SIZE_GUIDANCE) event_acc.Reload() @@ -167,7 +159,7 @@ def tflog2pandas(path: str) -> pd.DataFrame: # Dirty catch of DataLossError except Exception: LOGGER.text( - "Event file possibly corrupt: {}".format(path), + f"Event file possibly corrupt: {path}", level=LoggerObserver.WARN, ) traceback.print_exc() diff --git a/theseus/base/utilities/loggers/wandb_logger.py b/theseus/base/utilities/loggers/wandb_logger.py index 10754afd..f8f37d51 100644 --- a/theseus/base/utilities/loggers/wandb_logger.py +++ b/theseus/base/utilities/loggers/wandb_logger.py @@ -1,9 +1,7 @@ -from typing import Dict +import contextlib -try: +with contextlib.suppress(ModuleNotFoundError): import wandb as wandb_logger -except ModuleNotFoundError: - pass import os.path as osp @@ -28,7 +26,7 @@ def __init__( run_name: str, group_name: str = None, save_dir: str = None, - config_dict: Dict = None, + config_dict: dict = None, **kwargs, ): self.project_name = project_name @@ -48,7 +46,7 @@ def __init__( name=run_name, config=config_dict, group=self.group_name, - job_type=kwargs.get("job_type", None), + job_type=kwargs.get("job_type"), tags=tags, dir=self.save_dir, id=self.id, @@ -106,7 +104,7 @@ def log_figure(self, tag, value, step=0, **kwargs): wandb_logger.log({tag: image, "iterations": step}) else: wandb_logger.log({tag: value, "iterations": step}) - except Exception as e: + except Exception: pass def log_torch_module(self, tag, value, log_freq, **kwargs): @@ -200,7 +198,7 @@ def log_embedding( if metadata is not None and metadata_header is not None: for meta in metadata: for idx, item in enumerate(meta): - if metadata_header[idx] not in df_dict.keys(): + if metadata_header[idx] not in df_dict: df_dict[metadata_header[idx]] = [] df_dict[metadata_header[idx]].append(item) if label_img is not None: @@ -225,6 +223,6 @@ def find_run_id(dirname): if not osp.isfile(wandb_id_file): raise ValueError(f"Wandb ID file not found in {wandb_id_file}") else: - with open(wandb_id_file, "r") as f: + with open(wandb_id_file) as f: wandb_id = f.read().rstrip() return wandb_id diff --git a/theseus/base/utilities/optuna_tuner.py b/theseus/base/utilities/optuna_tuner.py index f35079e3..e0bab630 100644 --- a/theseus/base/utilities/optuna_tuner.py +++ b/theseus/base/utilities/optuna_tuner.py @@ -3,7 +3,9 @@ from copy import deepcopy import optuna +import wandb from omegaconf import DictConfig, OmegaConf +from optuna.integration.wandb import WeightsAndBiasesCallback from optuna.visualization import ( plot_contour, plot_edf, @@ -28,6 +30,7 @@ def __init__( pruner=None, sampler=None, save_dir: str = None, + wandb_kwargs: dict = None, # <-- Add this argument ) -> None: self.logger = LoggerObserver.getLogger("main") @@ -38,6 +41,12 @@ def __init__( self.pruner = pruner self.sampler = sampler self.save_dir = save_dir + self.wandb_kwargs = wandb_kwargs + self.WANDB_CALLBACK = None + if wandb_kwargs is not None: + self.WANDB_CALLBACK = WeightsAndBiasesCallback( + metric_name="auc", wandb_kwargs=wandb_kwargs, as_multirun=True + ) if save_dir is not None: os.makedirs(save_dir, exist_ok=True) @@ -55,21 +64,31 @@ def tune( config: DictConfig, pipeline_class: BasePipeline, optuna_callback: callable = None, - trial_user_attrs: dict = {}, + trial_user_attrs: dict = None, ): - if "optuna" not in config.keys(): + if trial_user_attrs is None: + trial_user_attrs = {} + if "optuna" not in config: self.logger.text( "Optuna key not found in config. Exit optuna", level=LoggerObserver.CRITICAL, ) raise ValueError() - wrapped_objective = lambda trial: self.objective( - trial, config, pipeline_class, trial_user_attrs, optuna_callback - ) + def wrapped_objective(trial): + return self.objective( + trial, config, pipeline_class, trial_user_attrs, optuna_callback + ) - self.study.optimize(wrapped_objective, n_trials=self.n_trials) + callbacks = None + if self.WANDB_CALLBACK is not None: + decorator = self.WANDB_CALLBACK.track_in_wandb() + wrapped_objective = decorator(wrapped_objective) + callbacks = [self.WANDB_CALLBACK] + self.study.optimize( + wrapped_objective, n_trials=self.n_trials, timeout=12 * 60 * 60, callbacks=callbacks + ) best_trial = self.study.best_trial self.save_best_config(self.save_dir, config, best_trial.params) self._rename_params() @@ -85,12 +104,10 @@ def save_best_config(self, save_dir: str, config: DictConfig, best_params: dict) save_dir = osp.join(save_dir, "best_configs") os.makedirs(save_dir, exist_ok=True) - with open(os.path.join(save_dir, "best_pipeline.yaml"), "w") as f: + with open(os.path.join(save_dir, f"{self.study_name}.yaml"), "w") as f: OmegaConf.save(config=config, f=f) - self.logger.text( - f"Best configuration saved at {save_dir}", level=LoggerObserver.INFO - ) + self.logger.text(f"Best configuration saved at {save_dir}", level=LoggerObserver.INFO) def _override_dict_with_optuna( self, trial, config: DictConfig, param_str: str, variable_type: str @@ -139,19 +156,19 @@ def objective( trial: optuna.Trial, config: DictConfig, pipeline_class: BasePipeline, - trial_user_attrs: dict = {}, + trial_user_attrs: dict = None, optuna_callback: callable = None, ): """Define the objective function""" # Override config with optuna trials values + if trial_user_attrs is None: + trial_user_attrs = {} tmp_config = deepcopy(config) optuna_params = tmp_config["optuna"] - for variable_type in optuna_params.keys(): + for variable_type in optuna_params: for param_str in optuna_params[variable_type]: - self._override_dict_with_optuna( - trial, tmp_config, param_str, variable_type - ) + self._override_dict_with_optuna(trial, tmp_config, param_str, variable_type) # Set fixed run's config for key, value in trial_user_attrs.items(): @@ -170,13 +187,21 @@ def objective( ) # Start training and evaluation - pipeline.fit() - score_dict = pipeline.evaluate() + try: + pipeline.fit() + score_dict = pipeline.evaluate() + except Exception as e: + self.logger.text( + f"Trial {trial.number} failed with exception: {e}", + level=LoggerObserver.ERROR, + ) + score_dict = {} + del tmp_config - best_key = trial_user_attrs.get("best_key", None) + best_key = trial_user_attrs.get("best_key") if best_key is not None: - return float(score_dict[best_key]) + return float(score_dict.get(best_key, -1)) return score_dict def callback_hook(self, trial, init_trainer_function, callback_fn): @@ -198,9 +223,7 @@ def _rename_params(self): if common_prefix != "": for trial_param_name in trial_param_names: new_param_name = trial_param_name.replace(common_prefix, "") - trial.params.update( - {new_param_name: trial.params[trial_param_name]} - ) + trial.params.update({new_param_name: trial.params[trial_param_name]}) trial.distributions.update( {new_param_name: trial.distributions[trial_param_name]} ) @@ -213,9 +236,11 @@ def leaderboard(self): df.columns = [col.replace("user_attrs_", "") for col in df.columns] return df - def visualize(self, plot: str, plot_params: dict = {}): + def visualize(self, plot: str, plot_params: dict = None): """Visualize everything""" + if plot_params is None: + plot_params = {} allow_plot_types = [ "history", "contour", @@ -247,9 +272,11 @@ def visualize(self, plot: str, plot_params: dict = {}): one_fig = self.visualize(plot_type, plot_params) fig.append((plot_type, one_fig)) else: - self.logger.text( - f"{plot} is not supported by Optuna", level=LoggerObserver.ERROR - ) + self.logger.text(f"{plot} is not supported by Optuna", level=LoggerObserver.ERROR) raise ValueError() + # Log to wandb if enabled + if plot != "all" and self.wandb_kwargs is not None and fig is not None: + wandb.log({f"Plot/{plot}": fig}) + return fig diff --git a/theseus/cv/base/augmentations/albumentation.py b/theseus/cv/base/augmentations/albumentation.py index 8c56394b..63aee0d1 100644 --- a/theseus/cv/base/augmentations/albumentation.py +++ b/theseus/cv/base/augmentations/albumentation.py @@ -4,7 +4,6 @@ GaussianBlur, HorizontalFlip, HueSaturationValue, - IAASharpen, LongestMaxSize, MedianBlur, MotionBlur, @@ -15,6 +14,7 @@ RandomSizedCrop, Resize, RGBShift, + Sharpen, ShiftScaleRotate, SmallestMaxSize, VerticalFlip, @@ -40,6 +40,6 @@ TRANSFORM_REGISTRY.register(VerticalFlip, prefix="Alb") TRANSFORM_REGISTRY.register(HueSaturationValue, prefix="Alb") TRANSFORM_REGISTRY.register(RandomSizedCrop, prefix="Alb") -TRANSFORM_REGISTRY.register(IAASharpen, prefix="Alb") +TRANSFORM_REGISTRY.register(Sharpen, prefix="Alb") TRANSFORM_REGISTRY.register(ToTensorV2, prefix="Alb") TRANSFORM_REGISTRY.register(LongestMaxSize, prefix="Alb") diff --git a/theseus/cv/base/utilities/visualization/utils.py b/theseus/cv/base/utilities/visualization/utils.py index fe4d2c1a..23c7936b 100644 --- a/theseus/cv/base/utilities/visualization/utils.py +++ b/theseus/cv/base/utilities/visualization/utils.py @@ -30,7 +30,7 @@ def get_font_size(image, text, polygons, font_type): fontsize = 1 # starting font size polywidth = polygons[1][0] - polygons[0][0] - imagewidth = image.shape[1] + image.shape[1] # portion of image width you want text width to be img_fraction = 1 diff --git a/theseus/cv/base/utilities/visualization/visualizer.py b/theseus/cv/base/utilities/visualization/visualizer.py index 264cb322..59f71e7d 100644 --- a/theseus/cv/base/utilities/visualization/visualizer.py +++ b/theseus/cv/base/utilities/visualization/visualizer.py @@ -1,5 +1,5 @@ import random -from typing import Any, List, Optional, Tuple, Union +from typing import Any import cv2 import numpy as np @@ -22,7 +22,7 @@ class Visualizer: r"""Visualizer class that do all the visualization stuffs""" def __init__(self): - self.image: Optional[np.ndarray] = None + self.image: np.ndarray | None = None self.class_names = None self.set_color(color_list) @@ -38,7 +38,7 @@ def set_image(self, image: np.ndarray) -> None: if self.image.dtype == "uint8": self.image = self.image / 255.0 - def set_classnames(self, class_names: List[str]) -> None: + def set_classnames(self, class_names: list[str]) -> None: self.class_names = class_names def get_image(self) -> np.ndarray: @@ -64,12 +64,11 @@ def draw_label( label: str, font: Any = cv2.FONT_HERSHEY_SIMPLEX, fontScale: int = 2, - fontColor: Tuple = (0, 0, 1), + fontColor: tuple = (0, 0, 1), thickness: int = 3, - outline: Tuple = (0, 0, 0), + outline: tuple = (0, 0, 0), offset: int = 50, ): - """ Draw text on the image then return @@ -174,9 +173,12 @@ def draw_bbox(self, boxes, labels=None, scores=None) -> None: color = self.color_list[1] coord = [box[0], box[1], box[2], box[3]] - c1, c2 = (int(coord[0]), int(coord[1])), ( - int(coord[2]), - int(coord[3]), + c1, c2 = ( + (int(coord[0]), int(coord[1])), + ( + int(coord[2]), + int(coord[3]), + ), ) cv2.rectangle(self.image, c1, c2, color, thickness=tl * 2) @@ -189,9 +191,7 @@ def draw_bbox(self, boxes, labels=None, scores=None) -> None: label = f"{label}: {score}" tf = max(tl - 2, 1) # font thickness - s_size = cv2.getTextSize( - f"{label}", 0, fontScale=float(tl) / 3, thickness=tf - )[0] + s_size = cv2.getTextSize(f"{label}", 0, fontScale=float(tl) / 3, thickness=tf)[0] c2 = c1[0] + s_size[0] + 15, c1[1] - s_size[1] - 3 cv2.rectangle(self.image, c1, c2, color, -1) # filled cv2.putText( @@ -213,8 +213,8 @@ def _tensor_to_numpy(self, image: torch.Tensor) -> np.ndarray: def make_grid( self, - batch: List[torch.Tensor], - nrow: Optional[int] = None, + batch: list[torch.Tensor], + nrow: int | None = None, normalize: bool = False, ) -> torch.Tensor: """ @@ -239,15 +239,19 @@ def make_grid( def denormalize( self, - image: Union[torch.Tensor, np.ndarray], - mean: List[float] = [0.485, 0.456, 0.406], - std: List[float] = [0.229, 0.224, 0.225], + image: torch.Tensor | np.ndarray, + mean: list[float] = None, + std: list[float] = None, ) -> np.ndarray: """ Denormalize an image and return image: `torch.Tensor` or `np.ndarray` image to be denormalized """ + if std is None: + std = [0.229, 0.224, 0.225] + if mean is None: + mean = [0.485, 0.456, 0.406] mean = np.array(mean) std = np.array(std) @@ -288,9 +292,7 @@ def denormalize_bboxes( return boxes - def decode_segmap( - self, segmap: np.ndarray, num_classes: Optional[int] = None - ) -> np.ndarray: + def decode_segmap(self, segmap: np.ndarray, num_classes: int | None = None) -> np.ndarray: """ Decode an segmentation mask into colored mask based on class indices diff --git a/theseus/cv/classification/augmentations/custom.py b/theseus/cv/classification/augmentations/custom.py index 925c7181..c983c339 100644 --- a/theseus/cv/classification/augmentations/custom.py +++ b/theseus/cv/classification/augmentations/custom.py @@ -5,11 +5,10 @@ import math from collections import namedtuple -from typing import Tuple import numpy as np import torch -from albumentations.core.bbox_utils import denormalize_bbox, normalize_bbox +from albumentations.core.bbox_utils import denormalize_bboxes, normalize_bboxes from albumentations.core.transforms_interface import DualTransform from torch import Tensor from torchvision.transforms import functional as F @@ -41,7 +40,7 @@ def __init__( :param min_cutout_size: minimum size of cutout (192 x 192) :param max_cutout_size: maximum size of cutout (512 x 512) """ - super(CustomCutout, self).__init__(always_apply, p) # Initialize parent class + super().__init__(always_apply, p) # Initialize parent class self.fill_value = fill_value self.bbox_removal_threshold = bbox_removal_threshold self.min_cutout_size = min_cutout_size @@ -88,10 +87,8 @@ def apply(self, image, **params): """ image = image.copy() # Don't change the original image self.img_height, self.img_width, _ = image.shape - for i in range(self.number): - cutout_arr, cutout_size, cutout_pos = self._get_cutout( - self.img_height, self.img_width - ) + for _i in range(self.number): + cutout_arr, cutout_size, cutout_pos = self._get_cutout(self.img_height, self.img_width) # Set to instance variables to use this later self.image = image @@ -114,10 +111,10 @@ def apply_to_bbox(self, bbox, **params): """ # Denormalize the bbox coordinates - bbox = denormalize_bbox(bbox, self.img_height, self.img_width) + bbox = denormalize_bboxes(bbox, self.img_height, self.img_width) x_min, y_min, x_max, y_max = tuple(map(int, bbox)) if x_min >= x_max or y_min >= y_max: - return normalize_bbox((0, 0, 0, 0), self.img_height, self.img_width) + return normalize_bboxes((0, 0, 0, 0), self.img_height, self.img_width) bbox_size = (x_max - x_min) * (y_max - y_min) # width * height overlapping_size = np.sum( @@ -127,9 +124,9 @@ def apply_to_bbox(self, bbox, **params): ) # Remove the bbox if it has more than some threshold of content is inside the cutout patch if overlapping_size / bbox_size > self.bbox_removal_threshold: - return normalize_bbox((0, 0, 0, 0), self.img_height, self.img_width) + return normalize_bboxes((0, 0, 0, 0), self.img_height, self.img_width) - return normalize_bbox(bbox, self.img_height, self.img_width) + return normalize_bboxes(bbox, self.img_height, self.img_width) def get_transform_init_args_names(self): """ @@ -166,9 +163,7 @@ def __init__( inplace: bool = False, ) -> None: super().__init__() - assert ( - num_classes > 0 - ), "Please provide a valid positive value for the num_classes." + assert num_classes > 0, "Please provide a valid positive value for the num_classes." assert alpha > 0, "Alpha param can't be zero." self.num_classes = num_classes @@ -176,7 +171,7 @@ def __init__( self.alpha = alpha self.inplace = inplace - def forward(self, batch: Tensor, target: Tensor) -> Tuple[Tensor, Tensor]: + def forward(self, batch: Tensor, target: Tensor) -> tuple[Tensor, Tensor]: """ Args: batch (Tensor): Float tensor of size (B, C, H, W) @@ -185,26 +180,22 @@ def forward(self, batch: Tensor, target: Tensor) -> Tuple[Tensor, Tensor]: Tensor: Randomly transformed batch. """ if batch.ndim != 4: - raise ValueError("Batch ndim should be 4. Got {}".format(batch.ndim)) + raise ValueError(f"Batch ndim should be 4. Got {batch.ndim}") if target.ndim != 1: - raise ValueError("Target ndim should be 1. Got {}".format(target.ndim)) + raise ValueError(f"Target ndim should be 1. Got {target.ndim}") if not batch.is_floating_point(): - raise TypeError( - "Batch dtype should be a float tensor. Got {}.".format(batch.dtype) - ) + raise TypeError(f"Batch dtype should be a float tensor. Got {batch.dtype}.") if target.dtype != torch.int64: - raise TypeError( - "Target dtype should be torch.int64. Got {}".format(target.dtype) - ) + raise TypeError(f"Target dtype should be torch.int64. Got {target.dtype}") if not self.inplace: batch = batch.clone() target = target.clone() if target.ndim == 1: - target = torch.nn.functional.one_hot( - target, num_classes=self.num_classes - ).to(dtype=batch.dtype) + target = torch.nn.functional.one_hot(target, num_classes=self.num_classes).to( + dtype=batch.dtype + ) if torch.rand(1).item() >= self.p: return batch, target @@ -214,9 +205,7 @@ def forward(self, batch: Tensor, target: Tensor) -> Tuple[Tensor, Tensor]: target_rolled = target.roll(1, 0) # Implemented as on mixup paper, page 3. - lambda_param = float( - torch._sample_dirichlet(torch.tensor([self.alpha, self.alpha]))[0] - ) + lambda_param = float(torch._sample_dirichlet(torch.tensor([self.alpha, self.alpha]))[0]) batch_rolled.mul_(1.0 - lambda_param) batch.mul_(lambda_param).add_(batch_rolled) @@ -256,9 +245,7 @@ def __init__( inplace: bool = False, ) -> None: super().__init__() - assert ( - num_classes > 0 - ), "Please provide a valid positive value for the num_classes." + assert num_classes > 0, "Please provide a valid positive value for the num_classes." assert alpha > 0, "Alpha param can't be zero." self.num_classes = num_classes @@ -266,7 +253,7 @@ def __init__( self.alpha = alpha self.inplace = inplace - def forward(self, batch: Tensor, target: Tensor) -> Tuple[Tensor, Tensor]: + def forward(self, batch: Tensor, target: Tensor) -> tuple[Tensor, Tensor]: """ Args: batch (Tensor): Float tensor of size (B, C, H, W) @@ -275,26 +262,22 @@ def forward(self, batch: Tensor, target: Tensor) -> Tuple[Tensor, Tensor]: Tensor: Randomly transformed batch. """ if batch.ndim != 4: - raise ValueError("Batch ndim should be 4. Got {}".format(batch.ndim)) + raise ValueError(f"Batch ndim should be 4. Got {batch.ndim}") if target.ndim != 1: - raise ValueError("Target ndim should be 1. Got {}".format(target.ndim)) + raise ValueError(f"Target ndim should be 1. Got {target.ndim}") if not batch.is_floating_point(): - raise TypeError( - "Batch dtype should be a float tensor. Got {}.".format(batch.dtype) - ) + raise TypeError(f"Batch dtype should be a float tensor. Got {batch.dtype}.") if target.dtype != torch.int64: - raise TypeError( - "Target dtype should be torch.int64. Got {}".format(target.dtype) - ) + raise TypeError(f"Target dtype should be torch.int64. Got {target.dtype}") if not self.inplace: batch = batch.clone() target = target.clone() if target.ndim == 1: - target = torch.nn.functional.one_hot( - target, num_classes=self.num_classes - ).to(dtype=batch.dtype) + target = torch.nn.functional.one_hot(target, num_classes=self.num_classes).to( + dtype=batch.dtype + ) if torch.rand(1).item() >= self.p: return batch, target @@ -304,9 +287,7 @@ def forward(self, batch: Tensor, target: Tensor) -> Tuple[Tensor, Tensor]: target_rolled = target.roll(1, 0) # Implemented as on cutmix paper, page 12 (with minor corrections on typos). - lambda_param = float( - torch._sample_dirichlet(torch.tensor([self.alpha, self.alpha]))[0] - ) + lambda_param = float(torch._sample_dirichlet(torch.tensor([self.alpha, self.alpha]))[0]) W, H = F.get_image_size(batch) r_x = torch.randint(W, (1,)) diff --git a/theseus/cv/classification/callbacks/gradcam_callback.py b/theseus/cv/classification/callbacks/gradcam_callback.py index 9f9160c0..55abff1e 100644 --- a/theseus/cv/classification/callbacks/gradcam_callback.py +++ b/theseus/cv/classification/callbacks/gradcam_callback.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Optional +from typing import Any import lightning.pytorch as pl import matplotlib.pyplot as plt @@ -25,10 +25,14 @@ class GradCAMVisualizationCallback(Callback): def __init__( self, - mean: List[float] = [0.485, 0.456, 0.406], - std: List[float] = [0.229, 0.224, 0.225], - **kwargs + mean: list[float] = None, + std: list[float] = None, + **kwargs, ) -> None: + if std is None: + std = [0.229, 0.224, 0.225] + if mean is None: + mean = [0.485, 0.456, 0.406] super().__init__() self.visualizer = Visualizer() self.mean = mean diff --git a/theseus/cv/classification/callbacks/visualize_callback.py b/theseus/cv/classification/callbacks/visualize_callback.py index d63cd88f..19703e9a 100644 --- a/theseus/cv/classification/callbacks/visualize_callback.py +++ b/theseus/cv/classification/callbacks/visualize_callback.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List +from typing import Any import lightning.pytorch as pl import matplotlib.pyplot as plt @@ -10,7 +10,6 @@ from theseus.base.utilities.cuda import move_to from theseus.base.utilities.loggers.observer import LoggerObserver from theseus.cv.base.utilities.visualization.visualizer import Visualizer -from theseus.cv.classification.utilities.gradcam import CAMWrapper, show_cam_on_image LOGGER = LoggerObserver.getLogger("main") @@ -26,20 +25,21 @@ class ClassificationVisualizerCallback(Callback): def __init__( self, - mean: List[float] = [0.485, 0.456, 0.406], - std: List[float] = [0.229, 0.224, 0.225], + mean: list[float] = None, + std: list[float] = None, **kwargs, ) -> None: + if std is None: + std = [0.229, 0.224, 0.225] + if mean is None: + mean = [0.485, 0.456, 0.406] super().__init__() self.visualizer = Visualizer() self.mean = mean self.std = std - def on_sanity_check_start( - self, trainer: pl.Trainer, pl_module: pl.LightningModule - ) -> None: - + def on_sanity_check_start(self, trainer: pl.Trainer, pl_module: pl.LightningModule) -> None: """ Sanitycheck before starting. Run only when debug=True """ @@ -52,7 +52,7 @@ def on_sanity_check_start( val_batch = next(iter(valloader)) try: self.visualize_model(model, train_batch) - except TypeError as e: + except TypeError: LOGGER.text("Cannot log model architecture", level=LoggerObserver.ERROR) self.visualize_gt(train_batch, val_batch, iters) @@ -87,7 +87,7 @@ def visualize_gt(self, train_batch, val_batch, iters): images = train_batch["inputs"].cpu() batch = [] - for idx, inputs in enumerate(images): + for _idx, inputs in enumerate(images): img_show = self.visualizer.denormalize(inputs) img_cam = TFF.to_tensor(img_show) batch.append(img_cam) @@ -112,7 +112,7 @@ def visualize_gt(self, train_batch, val_batch, iters): images = val_batch["inputs"].cpu() batch = [] - for idx, inputs in enumerate(images): + for _idx, inputs in enumerate(images): img_show = self.visualizer.denormalize(inputs, mean=self.mean, std=self.std) img_cam = TFF.to_tensor(img_show) batch.append(img_cam) diff --git a/theseus/cv/classification/datasets/csv_dataset.py b/theseus/cv/classification/datasets/csv_dataset.py index 7dc522fb..af1bcff5 100644 --- a/theseus/cv/classification/datasets/csv_dataset.py +++ b/theseus/cv/classification/datasets/csv_dataset.py @@ -1,4 +1,3 @@ -from typing import List, Optional import pandas as pd @@ -34,10 +33,10 @@ def __init__( image_dir: str, csv_path: str, txt_classnames: str, - transform: Optional[List] = None, - **kwargs + transform: list | None = None, + **kwargs, ): - super(ClassificationCSVDataset, self).__init__(**kwargs) + super().__init__(**kwargs) self.image_dir = image_dir self.txt_classnames = txt_classnames self.csv_path = csv_path @@ -49,7 +48,7 @@ def _load_data(self): Read data from csv and load into memory """ - with open(self.txt_classnames, "r") as f: + with open(self.txt_classnames) as f: self.classnames = f.read().splitlines() # Mapping between classnames and indices diff --git a/theseus/cv/classification/datasets/dataset.py b/theseus/cv/classification/datasets/dataset.py index e140e0d3..76936698 100644 --- a/theseus/cv/classification/datasets/dataset.py +++ b/theseus/cv/classification/datasets/dataset.py @@ -1,6 +1,4 @@ -import os import os.path as osp -from typing import Dict, List import torch from PIL import Image @@ -10,7 +8,7 @@ class ClassificationDataset(torch.utils.data.Dataset): r"""Base dataset for classification tasks""" def __init__(self, **kwargs): - super(ClassificationDataset, self).__init__(**kwargs) + super().__init__(**kwargs) self.classes_idx = {} self.classnames = None self.transform = None @@ -20,7 +18,7 @@ def __init__(self, **kwargs): def _load_data(self): raise NotImplementedError - def __getitem__(self, idx: int) -> Dict: + def __getitem__(self, idx: int) -> dict: """ Get one item """ @@ -48,7 +46,7 @@ def __getitem__(self, idx: int) -> Dict: def __len__(self) -> int: return len(self.fns) - def collate_fn(self, batch: List): + def collate_fn(self, batch: list): """ Collator for wrapping a batch """ diff --git a/theseus/cv/classification/datasets/folder_dataset.py b/theseus/cv/classification/datasets/folder_dataset.py index 93d7c1bf..b09730b3 100644 --- a/theseus/cv/classification/datasets/folder_dataset.py +++ b/theseus/cv/classification/datasets/folder_dataset.py @@ -1,7 +1,4 @@ import os -from typing import List, Optional - -import pandas as pd from theseus.base.utilities.loggers.observer import LoggerObserver @@ -32,13 +29,9 @@ class ClassificationImageFolderDataset(ClassificationDataset): """ def __init__( - self, - image_dir: str, - txt_classnames: str, - transform: Optional[List] = None, - **kwargs + self, image_dir: str, txt_classnames: str, transform: list | None = None, **kwargs ): - super(ClassificationImageFolderDataset, self).__init__(**kwargs) + super().__init__(**kwargs) self.image_dir = image_dir self.txt_classnames = txt_classnames self.transform = transform @@ -50,7 +43,7 @@ def _load_data(self): """ # Get classnames - with open(self.txt_classnames, "r") as f: + with open(self.txt_classnames) as f: self.classnames = f.read().splitlines() # Mapping between classnames and indices diff --git a/theseus/cv/classification/datasets/mixupcutmix_collator.py b/theseus/cv/classification/datasets/mixupcutmix_collator.py index 0a4de6e0..1a5c05ad 100644 --- a/theseus/cv/classification/datasets/mixupcutmix_collator.py +++ b/theseus/cv/classification/datasets/mixupcutmix_collator.py @@ -1,4 +1,3 @@ -from typing import List import numpy as np import torch @@ -15,18 +14,16 @@ def __init__( dataset: torch.utils.data.Dataset, mixup_alpha: float = 0.2, cutmix_alpha: float = 1.0, - weight: List[float] = [0.5, 0.5], - **kwargs + weight: list[float] = None, + **kwargs, ) -> None: + if weight is None: + weight = [0.5, 0.5] assert sum(weight) <= 1.0, "Sum of weight should be smaller than 1.0" self.mixup_transforms = [] - self.mixup_transforms.append( - RandomMixup(dataset.num_classes, p=1.0, alpha=mixup_alpha) - ) - self.mixup_transforms.append( - RandomCutmix(dataset.num_classes, p=1.0, alpha=cutmix_alpha) - ) + self.mixup_transforms.append(RandomMixup(dataset.num_classes, p=1.0, alpha=mixup_alpha)) + self.mixup_transforms.append(RandomCutmix(dataset.num_classes, p=1.0, alpha=cutmix_alpha)) self.mixup_transforms.append(None) self.weight = weight self.weight.append(1.0 - sum(weight)) diff --git a/theseus/cv/classification/metrics/errorcases.py b/theseus/cv/classification/metrics/errorcases.py index 333cbd29..16bd9141 100644 --- a/theseus/cv/classification/metrics/errorcases.py +++ b/theseus/cv/classification/metrics/errorcases.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Optional +from typing import Any import matplotlib.pyplot as plt from torchvision.transforms import functional as TFF @@ -20,7 +20,7 @@ class ErrorCases(Metric): def __init__( self, max_samples: int = 64, - classnames: Optional[List[str]] = None, + classnames: list[str] | None = None, label_type: str = "multiclass", **kwargs, ): @@ -33,7 +33,7 @@ def __init__( self.threshold = kwargs.get("threshold", 0.5) self.reset() - def update(self, outputs: Dict[str, Any], batch: Dict[str, Any]): + def update(self, outputs: dict[str, Any], batch: dict[str, Any]): """ Perform calculation based on prediction and targets """ @@ -56,7 +56,7 @@ def update(self, outputs: Dict[str, Any], batch: Dict[str, Any]): targets = targets.cpu().numpy().tolist() probs = probs.numpy().tolist() - for (output, target, prob, image) in zip(outputs, targets, probs, images): + for output, target, prob, image in zip(outputs, targets, probs, images): if output != target: self.images.append(image.cpu()) self.preds.append(output) @@ -68,16 +68,14 @@ def value(self): Plot error cases to figure then return """ pred_batch = [] - for idx, (image, pred, target, prob) in enumerate( + for _idx, (image, pred, target, prob) in enumerate( zip(self.images, self.preds, self.targets, self.probs) ): img_show = self.visualizer.denormalize(image) self.visualizer.set_image(img_show) if self.type == "multilabel": - prob = ", ".join( - [str(round(prob[i], 3)) for i, c in enumerate(pred) if c] - ) + prob = ", ".join([str(round(prob[i], 3)) for i, c in enumerate(pred) if c]) else: prob = str(round(prob, 3)) @@ -86,12 +84,8 @@ def value(self): pred = self.classnames[pred] target = self.classnames[target] else: - pred = ", ".join( - [self.classnames[int(i)] for i, c in enumerate(pred) if c] - ) - target = ", ".join( - [self.classnames[int(i)] for i, c in enumerate(target) if c] - ) + pred = ", ".join([self.classnames[int(i)] for i, c in enumerate(pred) if c]) + target = ", ".join([self.classnames[int(i)] for i, c in enumerate(target) if c]) self.visualizer.draw_label( f"GT: {target}\nP: {pred}\nC: {prob}", diff --git a/theseus/cv/classification/metrics/projection.py b/theseus/cv/classification/metrics/projection.py index 2f7dba0d..158d3db8 100644 --- a/theseus/cv/classification/metrics/projection.py +++ b/theseus/cv/classification/metrics/projection.py @@ -1,6 +1,6 @@ import hashlib import os -from typing import Any, Dict +from typing import Any import cv2 import numpy as np @@ -35,14 +35,12 @@ def __init__(self, save_dir=".temp", has_labels=False, **kwargs): os.makedirs(self.save_dir, exist_ok=True) - def update(self, outputs: Dict[str, Any], batch: Dict[str, Any]): + def update(self, outputs: dict[str, Any], batch: dict[str, Any]): """ Perform calculation based on prediction and targets """ features = outputs["features"].detach().cpu().numpy() - predictions = ( - torch.argmax(outputs["outputs"].detach().cpu(), dim=1).numpy().tolist() - ) + predictions = torch.argmax(outputs["outputs"].detach().cpu(), dim=1).numpy().tolist() inputs = batch["inputs"] if self.has_labels: targets = batch["targets"].numpy().tolist() @@ -51,9 +49,7 @@ def update(self, outputs: Dict[str, Any], batch: Dict[str, Any]): for i, _ in enumerate(features): filename = hashlib.sha256(img_names[i].encode("utf-8")).hexdigest() pred_img = self.visualizer.denormalize(inputs[i]) - pred_img = cv2.resize( - pred_img, dsize=(64, 64), interpolation=cv2.INTER_CUBIC - ) + pred_img = cv2.resize(pred_img, dsize=(64, 64), interpolation=cv2.INTER_CUBIC) embedding_path = self.save_dir + r"/" + filename + "_feat.npy" image_path = self.save_dir + r"/" + filename + "_img.npy" @@ -98,7 +94,7 @@ def value(self): self.logger.log( [ { - "tag": f"Validation/projection", + "tag": "Validation/projection", "value": all_embeddings, "type": LoggerObserver.EMBED, "kwargs": { diff --git a/theseus/cv/classification/models/huggingface_models.py b/theseus/cv/classification/models/huggingface_models.py index 15dd9fc5..3c840b2c 100644 --- a/theseus/cv/classification/models/huggingface_models.py +++ b/theseus/cv/classification/models/huggingface_models.py @@ -1,5 +1,5 @@ from collections import OrderedDict -from typing import Any, Dict, List, Optional +from typing import Any import torch import torch.nn as nn @@ -28,10 +28,10 @@ def __init__( model_name: str, num_classes: int = 1000, from_pretrained: bool = True, - classnames: Optional[List] = None, + classnames: list | None = None, pooling: str = "first", freeze: bool = False, - **kwargs + **kwargs, ): super().__init__() self.name = model_name @@ -74,7 +74,7 @@ def get_model(self): """ return self.model - def forward_features(self, batch: Dict, device: torch.device = None): + def forward_features(self, batch: dict, device: torch.device = None): input_ids, attention_mask = batch["input_ids"], batch["attention_mask"] transformer_out = self.model(input_ids=input_ids, attention_mask=attention_mask) @@ -90,7 +90,7 @@ def forward_features(self, batch: Dict, device: torch.device = None): return features - def forward_batch(self, batch: Dict, device: torch.device = None): + def forward_batch(self, batch: dict, device: torch.device = None): if device is not None: batch = move_to(batch, device) features = self.forward_features(batch, device) @@ -98,7 +98,7 @@ def forward_batch(self, batch: Dict, device: torch.device = None): return {"outputs": outputs, "features": features} - def get_prediction(self, adict: Dict[str, Any], device: torch.device = None): + def get_prediction(self, adict: dict[str, Any], device: torch.device = None): """ Inference using the model. diff --git a/theseus/cv/classification/models/timm_models.py b/theseus/cv/classification/models/timm_models.py index c5dc562d..afd963d1 100644 --- a/theseus/cv/classification/models/timm_models.py +++ b/theseus/cv/classification/models/timm_models.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Optional +from typing import Any import timm import torch @@ -28,9 +28,9 @@ def __init__( model_name: str, num_classes: int = 1000, from_pretrained: bool = True, - classnames: Optional[List] = None, + classnames: list | None = None, freeze: bool = False, - **kwargs + **kwargs, ): super().__init__() self.name = model_name @@ -65,9 +65,7 @@ def __init__( ) self.features = None - self.pooling = torch.nn.Sequential( - nn.AdaptiveAvgPool2d((1, 1)), nn.Flatten() - ) + self.pooling = torch.nn.Sequential(nn.AdaptiveAvgPool2d((1, 1)), nn.Flatten()) if self.freeze: self.freeze_backbone() @@ -89,7 +87,7 @@ def get_model(self): """ return self.model - def forward_batch(self, batch: Dict, device: torch.device = None): + def forward_batch(self, batch: dict, device: torch.device = None): if device is not None: x = move_to(batch["inputs"], device) else: @@ -100,7 +98,7 @@ def forward_batch(self, batch: Dict, device: torch.device = None): self.features = outputs return {"outputs": outputs, "features": self.features} - def get_prediction(self, adict: Dict[str, Any], device: torch.device = None): + def get_prediction(self, adict: dict[str, Any], device: torch.device = None): """ Inference using the model. @@ -112,9 +110,7 @@ def get_prediction(self, adict: Dict[str, Any], device: torch.device = None): outputs = self.forward_batch(adict, device)["outputs"] if not adict.get("multilabel"): - outputs, probs = logits2labels( - outputs, label_type="multiclass", return_probs=True - ) + outputs, probs = logits2labels(outputs, label_type="multiclass", return_probs=True) else: outputs, probs = logits2labels( outputs, @@ -136,8 +132,7 @@ def get_prediction(self, adict: Dict[str, Any], device: torch.device = None): classnames = [self.classnames[int(clsid)] for clsid in classids] elif self.classnames and adict.get("multilabel"): classnames = [ - [self.classnames[int(i)] for i, c in enumerate(clsid) if c] - for clsid in classids + [self.classnames[int(i)] for i, c in enumerate(clsid) if c] for clsid in classids ] else: classnames = [] diff --git a/theseus/cv/classification/pipeline.py b/theseus/cv/classification/pipeline.py index d53ee068..10b2453b 100644 --- a/theseus/cv/classification/pipeline.py +++ b/theseus/cv/classification/pipeline.py @@ -15,7 +15,7 @@ class ClassificationPipeline(BasePipeline): """docstring for Pipeline.""" def __init__(self, opt: DictConfig): - super(ClassificationPipeline, self).__init__(opt) + super().__init__(opt) self.opt = opt def init_registry(self): diff --git a/theseus/cv/classification/utilities/gradcam.py b/theseus/cv/classification/utilities/gradcam.py index 62cf31af..eaf37adf 100644 --- a/theseus/cv/classification/utilities/gradcam.py +++ b/theseus/cv/classification/utilities/gradcam.py @@ -1,18 +1,11 @@ -from typing import Callable, List, Optional, Tuple import numpy as np import torch from pytorch_grad_cam import ( - AblationCAM, EigenCAM, - FullGrad, GradCAM, - GradCAMPlusPlus, - ScoreCAM, - XGradCAM, ) from pytorch_grad_cam.base_cam import BaseCAM -from pytorch_grad_cam.utils.image import show_cam_on_image from pytorch_grad_cam.utils.model_targets import ClassifierOutputTarget from theseus.base.utilities.loggers.observer import LoggerObserver @@ -56,21 +49,19 @@ class CAMWrapper(BaseCAM): def __init__( self, model: torch.nn.Module, - model_name: Optional[str] = None, - target_layers: Optional[List[torch.nn.Module]] = None, + model_name: str | None = None, + target_layers: list[torch.nn.Module] | None = None, **kwargs, ) -> None: - self.activations_and_grads = type( - "DummyClass", (object,), {"release": {lambda: None}} - )() + self.activations_and_grads = type("DummyClass", (object,), {"release": {lambda: None}})() - assert ( - model_name is not None or target_layers is not None - ), "Should specify model name or target layers name" + assert model_name is not None or target_layers is not None, ( + "Should specify model name or target layers name" + ) if target_layers is None: - for available_model in model_last_layers.keys(): + for available_model in model_last_layers: if model_name.startswith(available_model): model_name = available_model break @@ -85,12 +76,12 @@ def __init__( target_layers = get_layer_recursively(model, model_last_layers[model_name]) - super(CAMWrapper, self).__init__(model, target_layers, **kwargs) + super().__init__(model, target_layers, **kwargs) def forward( self, input_tensor: torch.Tensor, - targets: List[torch.nn.Module], + targets: list[torch.nn.Module], eigen_smooth: bool = False, return_probs: bool = False, ) -> np.ndarray: @@ -104,14 +95,10 @@ def forward( outputs = self.activations_and_grads(input_tensor) if targets is None: target_categories = np.argmax(outputs.cpu().data.numpy(), axis=-1) - targets = [ - ClassifierOutputTarget(category) for category in target_categories - ] + targets = [ClassifierOutputTarget(category) for category in target_categories] if return_probs: - scores = np.max( - torch.softmax(outputs, dim=-1).cpu().data.numpy(), axis=-1 - ) + scores = np.max(torch.softmax(outputs, dim=-1).cpu().data.numpy(), axis=-1) if self.uses_gradients: self.model.zero_grad() @@ -138,7 +125,7 @@ def forward( def __call__( self, input_tensor: torch.Tensor, - targets: List[torch.nn.Module] = None, + targets: list[torch.nn.Module] = None, aug_smooth: bool = False, eigen_smooth: bool = False, return_probs: bool = False, @@ -146,9 +133,7 @@ def __call__( # Smooth the CAM result with test time augmentation if aug_smooth is True: - return self.forward_augmentation_smoothing( - input_tensor, targets, eigen_smooth - ) + return self.forward_augmentation_smoothing(input_tensor, targets, eigen_smooth) return self.forward(input_tensor, targets, eigen_smooth, return_probs) diff --git a/theseus/cv/detection/augmentations/bbox_transforms.py b/theseus/cv/detection/augmentations/bbox_transforms.py index e8b03a63..3ef489b2 100644 --- a/theseus/cv/detection/augmentations/bbox_transforms.py +++ b/theseus/cv/detection/augmentations/bbox_transforms.py @@ -47,9 +47,7 @@ def __init__(self, order): Class construstor :param order: bbox format """ - super(BoxOrder, self).__init__( - always_apply=True, p=1.0 - ) # Initialize parent class + super().__init__(always_apply=True, p=1.0) # Initialize parent class self.order = order def apply(self, image, **params): @@ -62,7 +60,6 @@ def apply(self, image, **params): return image def apply_to_bbox(self, bbox, **params): - """ Change box order between (xmin, ymin, xmax, ymax) and (xcenter, ycenter, width, height). :param bbox: (tensor) or {np.array) bounding bbox, sized [N, 4] diff --git a/theseus/cv/detection/callbacks/visualization.py b/theseus/cv/detection/callbacks/visualization.py index b30e2ecf..9df7e40f 100644 --- a/theseus/cv/detection/callbacks/visualization.py +++ b/theseus/cv/detection/callbacks/visualization.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List +from typing import Any import lightning.pytorch as pl import matplotlib.patches as mpatches @@ -27,10 +27,14 @@ class DetectionVisualizerCallback(Callback): def __init__( self, order: str = None, - mean: List[float] = [0.485, 0.456, 0.406], - std: List[float] = [0.229, 0.224, 0.225], - **kwargs + mean: list[float] = None, + std: list[float] = None, + **kwargs, ) -> None: + if std is None: + std = [0.229, 0.224, 0.225] + if mean is None: + mean = [0.485, 0.456, 0.406] super().__init__() self.visualizer = Visualizer() self.order = order @@ -43,7 +47,6 @@ def on_sanity_check_start(self, trainer: pl.Trainer, pl_module: pl.LightningModu """ iters = trainer.iterations - model = pl_module.model valloader = pl_module.datamodule.valloader trainloader = pl_module.datamodule.trainloader train_batch = next(iter(trainloader)) @@ -64,7 +67,7 @@ def visualize_gt(self, train_batch, val_batch, iters, classnames): anns = train_batch["targets"] batch = [] - for idx, (inputs, ann) in enumerate(zip(images, anns)): + for _idx, (inputs, ann) in enumerate(zip(images, anns)): boxes = ann["boxes"] labels = ann["labels"].numpy() img_show = self.visualizer.denormalize(inputs, mean=self.mean, std=self.std) @@ -114,7 +117,7 @@ def visualize_gt(self, train_batch, val_batch, iters, classnames): anns = val_batch["targets"] batch = [] - for idx, (inputs, ann) in enumerate(zip(images, anns)): + for _idx, (inputs, ann) in enumerate(zip(images, anns)): boxes = ann["boxes"] labels = ann["labels"].numpy() img_show = self.visualizer.denormalize(inputs, mean=self.mean, std=self.std) @@ -193,7 +196,7 @@ def on_validation_end(self, trainer: pl.Trainer, pl_module: pl.LightningModule): preds = [i for i in zip(preds["boxes"], preds["confidences"], preds["labels"])] batch = [] - for idx, (inputs, target, pred) in enumerate(zip(images, targets, preds)): + for _idx, (inputs, target, pred) in enumerate(zip(images, targets, preds)): # Ground truth boxes = target["boxes"] labels = target["labels"].numpy() @@ -205,9 +208,7 @@ def on_validation_end(self, trainer: pl.Trainer, pl_module: pl.LightningModule): # Prediction boxes, scores, labels = pred - decode_pred = self.visualizer.denormalize( - inputs, mean=self.mean, std=self.std - ) + decode_pred = self.visualizer.denormalize(inputs, mean=self.mean, std=self.std) self.visualizer.set_image(decode_pred.copy()) self.visualizer.draw_bbox(boxes, labels=labels, scores=scores) decode_pred = self.visualizer.get_image() diff --git a/theseus/cv/detection/datasets/base.py b/theseus/cv/detection/datasets/base.py index 053b77a1..8bb51ffd 100644 --- a/theseus/cv/detection/datasets/base.py +++ b/theseus/cv/detection/datasets/base.py @@ -1,4 +1,3 @@ -from typing import Dict, List import torch @@ -19,7 +18,7 @@ def _load_data(self): def load_image_and_boxes(self, index): raise NotImplementedError - def __getitem__(self, idx: int) -> Dict: + def __getitem__(self, idx: int) -> dict: """ Get one item """ @@ -61,7 +60,7 @@ def __getitem__(self, idx: int) -> Dict: def __len__(self) -> int: return len(self.fns) - def collate_fn(self, batch: List): + def collate_fn(self, batch: list): """ Collator for wrapping a batch """ diff --git a/theseus/cv/detection/datasets/coco.py b/theseus/cv/detection/datasets/coco.py index 5b19b731..844e0e37 100644 --- a/theseus/cv/detection/datasets/coco.py +++ b/theseus/cv/detection/datasets/coco.py @@ -1,5 +1,4 @@ import os -from typing import List, Optional import cv2 import numpy as np @@ -24,13 +23,7 @@ class COCODataset(DetectionDataset): """ - def __init__( - self, - image_dir: str, - label_path: str, - transform: Optional[List] = None, - **kwargs - ): + def __init__(self, image_dir: str, label_path: str, transform: list | None = None, **kwargs): super().__init__(**kwargs) self.image_dir = image_dir self.label_path = label_path @@ -71,9 +64,7 @@ def load_image(self, image_index): def load_annotations(self, image_index, width, height): # get ground truth annotations - annotations_ids = self.fns.getAnnIds( - imgIds=self.image_ids[image_index], iscrowd=None - ) + annotations_ids = self.fns.getAnnIds(imgIds=self.image_ids[image_index], iscrowd=None) annotations = np.zeros((0, 5)) # some images appear to miss annotations @@ -82,8 +73,7 @@ def load_annotations(self, image_index, width, height): # parse annotations coco_annotations = self.fns.loadAnns(annotations_ids) - for idx, a in enumerate(coco_annotations): - + for _idx, a in enumerate(coco_annotations): # some annotations have basically no width / height, skip them if a["bbox"][2] <= 2 or a["bbox"][3] <= 2: continue diff --git a/theseus/cv/detection/metrics/map.py b/theseus/cv/detection/metrics/map.py index 4634f2df..65b4c8bf 100644 --- a/theseus/cv/detection/metrics/map.py +++ b/theseus/cv/detection/metrics/map.py @@ -86,8 +86,7 @@ def update(self, output, batch): gt_clss = gt["labels"].numpy().tolist() gt_instances = [ - BoxWithLabel(self.idx, box, int(cls), 1.0) - for box, cls in zip(gt_boxes, gt_clss) + BoxWithLabel(self.idx, box, int(cls), 1.0) for box, cls in zip(gt_boxes, gt_clss) ] pred_instances = [ BoxWithLabel(self.idx, box, int(cls), scr) @@ -120,7 +119,7 @@ def make_gt_json_file(self, path): instance_id = instance_info["image_id"] instance_name = instance_info["image_name"] height, width = instance_info["height"], instance_info["width"] - if instance_id not in self.image_id_dict.keys(): + if instance_id not in self.image_id_dict: self.image_id_dict[instance_id] = img_count img_count += 1 image_id = self.image_id_dict[instance_id] @@ -133,7 +132,6 @@ def make_gt_json_file(self, path): my_dict["images"].append(img_dict) for item in instance: - class_id = int(item.get_label()) xmin, ymin, xmax, ymax = item.get_box() @@ -204,8 +202,8 @@ def value(self): if len(results) == 0: # empty prediction return { - f"precision": 0, - f"recall": 0, + "precision": 0, + "recall": 0, "f1_score": 0, } diff --git a/theseus/cv/detection/metrics/misc.py b/theseus/cv/detection/metrics/misc.py index 0a7e5fc2..28f28e17 100644 --- a/theseus/cv/detection/metrics/misc.py +++ b/theseus/cv/detection/metrics/misc.py @@ -1,4 +1,3 @@ -from typing import Dict, List def bb_intersection_over_union(boxA, boxB): @@ -70,8 +69,8 @@ def find_best_matching_box(self, boxes, min_iou): class MatchingPairs: def __init__( self, - pred_boxes: List[BoxWithLabel], - gt_boxes: List[BoxWithLabel], + pred_boxes: list[BoxWithLabel], + gt_boxes: list[BoxWithLabel], min_iou=0.5, eps=0.000001, ) -> None: diff --git a/theseus/cv/detection/metrics/precision_recall.py b/theseus/cv/detection/metrics/precision_recall.py index 97065988..f2492191 100644 --- a/theseus/cv/detection/metrics/precision_recall.py +++ b/theseus/cv/detection/metrics/precision_recall.py @@ -1,4 +1,3 @@ -from typing import Dict, List import numpy as np @@ -31,8 +30,7 @@ def update(self, output, batch): gt_clss = gt["labels"].numpy().tolist() gt_instances = [ - BoxWithLabel(self.idx, box, int(cls), 1.0) - for box, cls in zip(gt_boxes, gt_clss) + BoxWithLabel(self.idx, box, int(cls), 1.0) for box, cls in zip(gt_boxes, gt_clss) ] pred_instances = [ BoxWithLabel(self.idx, box, int(cls), scr) @@ -50,16 +48,12 @@ def value(self): score = self.calculate_pr(total_tp, total_fp, total_fn) return score - def calculate_cfm( - self, pred_boxes: List[BoxWithLabel], gt_boxes: List[BoxWithLabel] - ): + def calculate_cfm(self, pred_boxes: list[BoxWithLabel], gt_boxes: list[BoxWithLabel]): total_fp = [] total_fn = [] total_tp = [] for pred_box, gt_box in zip(pred_boxes, gt_boxes): - matched_pairs = MatchingPairs( - pred_box, gt_box, min_iou=self.min_iou, eps=self.eps - ) + matched_pairs = MatchingPairs(pred_box, gt_box, min_iou=self.min_iou, eps=self.eps) tp = matched_pairs.get_acc() fp = matched_pairs.get_false_positive() fn = matched_pairs.get_false_negative() @@ -90,7 +84,6 @@ def calculate_pr(self, total_tp, total_fp, total_fn): precisions = [] recalls = [] for cls_id in range(self.num_classes): - if tp_per_class[cls_id] + fp_per_class[cls_id] == 0: precisions.append(-1) else: @@ -101,9 +94,7 @@ def calculate_pr(self, total_tp, total_fp, total_fn): if tp_per_class[cls_id] + fn_per_class[cls_id] == 0: recalls.append(-1) else: - recalls.append( - tp_per_class[cls_id] / (tp_per_class[cls_id] + fn_per_class[cls_id]) - ) + recalls.append(tp_per_class[cls_id] / (tp_per_class[cls_id] + fn_per_class[cls_id])) np_precisions = np.array(precisions) np_recalls = np.array(recalls) @@ -111,14 +102,12 @@ def calculate_pr(self, total_tp, total_fp, total_fn): precision_all = sum(np_precisions[np_precisions != -1]) / ( self.num_classes - sum(np_precisions == -1) ) - recall_all = sum(np_recalls[np_recalls != -1]) / ( - self.num_classes - sum(np_recalls == -1) - ) + recall_all = sum(np_recalls[np_recalls != -1]) / (self.num_classes - sum(np_recalls == -1)) f1_score = 2 * precision_all * recall_all / (precision_all + recall_all) return { - f"simple_precision": precision_all, - f"simple_recall": recall_all, + "simple_precision": precision_all, + "simple_recall": recall_all, "simple_f1_score": f1_score, } diff --git a/theseus/cv/detection/pipeline.py b/theseus/cv/detection/pipeline.py index c5845076..5b3c53ef 100644 --- a/theseus/cv/detection/pipeline.py +++ b/theseus/cv/detection/pipeline.py @@ -16,7 +16,7 @@ class DetectionPipeline(BasePipeline): """docstring for Pipeline.""" def __init__(self, opt: DictConfig): - super(DetectionPipeline, self).__init__(opt) + super().__init__(opt) self.opt = opt def init_registry(self): diff --git a/theseus/cv/semantic/augmentations/mosaic.py b/theseus/cv/semantic/augmentations/mosaic.py index dcff2a99..17f92fc0 100644 --- a/theseus/cv/semantic/augmentations/mosaic.py +++ b/theseus/cv/semantic/augmentations/mosaic.py @@ -1,5 +1,4 @@ import random -from typing import List, Tuple import numpy as np from albumentations import Compose, RandomCrop, Resize @@ -22,7 +21,7 @@ def __init__( self, width: int, height: int, - scale_range: Tuple[float, float] = (0.3, 0.7), + scale_range: tuple[float, float] = (0.3, 0.7), ) -> None: self.width = width self.height = height @@ -40,7 +39,7 @@ def get_resize(self, image: np.array, mask: np.array, width: int, height: int): item = transforms(image=image, mask=mask) return item["image"], item["mask"] - def __call__(self, set_images: List[np.array], set_masks: List[np.array]): + def __call__(self, set_images: list[np.array], set_masks: list[np.array]): """ set_images: `List[np.array]` batch of numpy images (H,W,3) @@ -69,16 +68,12 @@ def __call__(self, set_images: List[np.array], set_masks: List[np.array]): result_mask[:divid_point_y, :divid_point_x] = mask elif i == 1: # top-right - img, mask = self.get_resize( - img, mask, self.width - divid_point_x, divid_point_y - ) + img, mask = self.get_resize(img, mask, self.width - divid_point_x, divid_point_y) result_image[:divid_point_y, divid_point_x : self.width, :] = img result_mask[:divid_point_y, divid_point_x : self.width] = mask elif i == 2: # bottom-left - img, mask = self.get_resize( - img, mask, divid_point_x, self.height - divid_point_y - ) + img, mask = self.get_resize(img, mask, divid_point_x, self.height - divid_point_y) result_image[divid_point_y : self.height, :divid_point_x, :] = img result_mask[divid_point_y : self.height, :divid_point_x] = mask @@ -89,11 +84,7 @@ def __call__(self, set_images: List[np.array], set_masks: List[np.array]): self.width - divid_point_x, self.height - divid_point_y, ) - result_image[ - divid_point_y : self.height, divid_point_x : self.width, : - ] = img - result_mask[ - divid_point_y : self.height, divid_point_x : self.width - ] = mask + result_image[divid_point_y : self.height, divid_point_x : self.width, :] = img + result_mask[divid_point_y : self.height, divid_point_x : self.width] = mask return result_image, result_mask diff --git a/theseus/cv/semantic/callbacks/visualize_callbacks.py b/theseus/cv/semantic/callbacks/visualize_callbacks.py index cb5be960..85d0b482 100644 --- a/theseus/cv/semantic/callbacks/visualize_callbacks.py +++ b/theseus/cv/semantic/callbacks/visualize_callbacks.py @@ -1,4 +1,4 @@ -from typing import Any, Dict +from typing import Any import lightning.pytorch as pl import matplotlib.patches as mpatches @@ -75,7 +75,7 @@ def visualize_gt(self, train_batch, val_batch, iters, classnames): masks = train_batch["targets"].squeeze() batch = [] - for idx, (inputs, mask) in enumerate(zip(images, masks)): + for _idx, (inputs, mask) in enumerate(zip(images, masks)): img_show = self.visualizer.denormalize(inputs) decode_mask = self.visualizer.decode_segmap(mask.numpy()) img_show = TFF.to_tensor(img_show) @@ -119,7 +119,7 @@ def visualize_gt(self, train_batch, val_batch, iters, classnames): masks = val_batch["targets"].squeeze() batch = [] - for idx, (inputs, mask) in enumerate(zip(images, masks)): + for _idx, (inputs, mask) in enumerate(zip(images, masks)): img_show = self.visualizer.denormalize(inputs) decode_mask = self.visualizer.decode_segmap(mask.numpy()) img_show = TFF.to_tensor(img_show) @@ -190,7 +190,7 @@ def on_validation_end(self, trainer: pl.Trainer, pl_module: pl.LightningModule): preds = model.model.get_prediction({"inputs": images}, model.device)["masks"] batch = [] - for idx, (inputs, mask, pred) in enumerate(zip(images, masks, preds)): + for _idx, (inputs, mask, pred) in enumerate(zip(images, masks, preds)): img_show = self.visualizer.denormalize(inputs) decode_mask = self.visualizer.decode_segmap(mask.numpy()) decode_pred = self.visualizer.decode_segmap(pred) diff --git a/theseus/cv/semantic/datasets/csv_dataset.py b/theseus/cv/semantic/datasets/csv_dataset.py index 145943c6..e3ca1327 100644 --- a/theseus/cv/semantic/datasets/csv_dataset.py +++ b/theseus/cv/semantic/datasets/csv_dataset.py @@ -1,4 +1,3 @@ -from typing import List, Optional import numpy as np import pandas as pd @@ -35,10 +34,10 @@ def __init__( mask_dir: str, csv_path: str, txt_classnames: str, - transform: Optional[List] = None, - **kwargs + transform: list | None = None, + **kwargs, ): - super(SemanticCSVDataset, self).__init__(**kwargs) + super().__init__(**kwargs) self.image_dir = image_dir self.mask_dir = mask_dir self.csv_path = csv_path @@ -51,7 +50,7 @@ def _load_data(self): Read data from csv and load into memory """ - with open(self.txt_classnames, "r") as f: + with open(self.txt_classnames) as f: self.classnames = f.read().splitlines() # Mapping between classnames and indices diff --git a/theseus/cv/semantic/datasets/dataset.py b/theseus/cv/semantic/datasets/dataset.py index 7d21bb61..f6d485b6 100644 --- a/theseus/cv/semantic/datasets/dataset.py +++ b/theseus/cv/semantic/datasets/dataset.py @@ -1,5 +1,4 @@ import os -from typing import Dict import numpy as np import torch @@ -21,7 +20,7 @@ def __init__(self, **kwawrgs): def _load_data(self): raise NotImplementedError - def __getitem__(self, idx: int) -> Dict: + def __getitem__(self, idx: int) -> dict: """ Get one item """ diff --git a/theseus/cv/semantic/datasets/mosaic_collator.py b/theseus/cv/semantic/datasets/mosaic_collator.py index c67ca888..9e096622 100644 --- a/theseus/cv/semantic/datasets/mosaic_collator.py +++ b/theseus/cv/semantic/datasets/mosaic_collator.py @@ -1,5 +1,4 @@ import random -from typing import Dict, List, Tuple import numpy as np import torch @@ -20,13 +19,11 @@ class MosaicCollator(BaseCollator): """ - def __init__( - self, scale_range: Tuple[float, float] = (0.3, 0.7), p=0.5, **kwargs - ) -> None: + def __init__(self, scale_range: tuple[float, float] = (0.3, 0.7), p=0.5, **kwargs) -> None: self.scale_range = scale_range self.p = p - def __call__(self, batch: List[Dict]): + def __call__(self, batch: list[dict]): """ batch: `List[Dict]` batch of tensor images and mask # (B,3,H,W), (B,NC,H,W) @@ -65,12 +62,8 @@ def __call__(self, batch: List[Dict]): candidate_images = set_images[candidate_indices, :] candidate_masks = set_masks[candidate_indices, :] - candidate_images = torch.cat( - [candidate_images, current_image.unsqueeze(0)], dim=0 - ) - candidate_masks = torch.cat( - [candidate_masks, current_mask.unsqueeze(0)], dim=0 - ) + candidate_images = torch.cat([candidate_images, current_image.unsqueeze(0)], dim=0) + candidate_masks = torch.cat([candidate_masks, current_mask.unsqueeze(0)], dim=0) shuffling_id = torch.randperm(candidate_images.size()[0]) candidate_images = candidate_images[shuffling_id] diff --git a/theseus/cv/semantic/losses/ce_loss.py b/theseus/cv/semantic/losses/ce_loss.py index 41d9386f..8894292e 100644 --- a/theseus/cv/semantic/losses/ce_loss.py +++ b/theseus/cv/semantic/losses/ce_loss.py @@ -1,4 +1,3 @@ -from typing import Dict, List import torch from torch import nn @@ -10,13 +9,13 @@ class SemanticCELoss(nn.Module): r"""CELoss is warper of cross-entropy loss""" def __init__(self, weight=None, ignore_index=None, **kwargs): - super(SemanticCELoss, self).__init__() + super().__init__() self.weight = weight if self.weight is not None: self.weight = torch.FloatTensor(self.weight) self.ignore_index = ignore_index - def forward(self, outputs: Dict, batch: Dict, device: torch.device) -> torch.Tensor: + def forward(self, outputs: dict, batch: dict, device: torch.device) -> torch.Tensor: pred = outputs["outputs"] target = move_to(batch["targets"], device) @@ -42,23 +41,19 @@ class SemanticSmoothCELoss(nn.Module): r"""SmoothCELoss is warper of label smoothing cross-entropy loss""" def __init__(self, alpha=1e-6, ignore_index=None, reduction="mean", **kwargs): - super(SemanticSmoothCELoss, self).__init__() + super().__init__() self.ignore_index = ignore_index self.reduction = reduction self.alpha = alpha - def forward(self, outputs: Dict, batch: Dict, device: torch.device) -> torch.Tensor: + def forward(self, outputs: dict, batch: dict, device: torch.device) -> torch.Tensor: pred = outputs["outputs"] targets = move_to(batch["targets"], device) batch_size, num_classes = pred.shape[:2] - y_hot = move_to(torch.zeros(pred.shape), device).scatter_( - 1, targets.unsqueeze(1), 1.0 - ) + y_hot = move_to(torch.zeros(pred.shape), device).scatter_(1, targets.unsqueeze(1), 1.0) y_smooth = (1 - self.alpha) * y_hot + self.alpha / num_classes - loss = torch.sum( - -y_smooth * torch.nn.functional.log_softmax(pred, -1), -1 - ).sum() + loss = torch.sum(-y_smooth * torch.nn.functional.log_softmax(pred, -1), -1).sum() if self.reduction == "mean": loss /= batch_size @@ -80,11 +75,7 @@ class OhemCELoss(nn.Module): """ def __init__( - self, - ignore_label: int = 255, - weight: List = None, - thresh: float = 0.7, - **kwargs + self, ignore_label: int = 255, weight: list = None, thresh: float = 0.7, **kwargs ) -> None: super().__init__() @@ -98,7 +89,7 @@ def __init__( weight=self.weight, ignore_index=ignore_label, reduction="none" ) - def forward(self, outputs: Dict, batch: Dict, device: torch.device) -> torch.Tensor: + def forward(self, outputs: dict, batch: dict, device: torch.device) -> torch.Tensor: pred = outputs["outputs"] labels = move_to(batch["targets"], device) diff --git a/theseus/cv/semantic/losses/dice_loss.py b/theseus/cv/semantic/losses/dice_loss.py index 6a711dfe..3867cec0 100644 --- a/theseus/cv/semantic/losses/dice_loss.py +++ b/theseus/cv/semantic/losses/dice_loss.py @@ -1,4 +1,3 @@ -from typing import Dict, List import torch import torch.nn as nn @@ -16,19 +15,19 @@ class DiceLoss(nn.Module): """ def __init__(self, eps=1e-6, **kwargs): - super(DiceLoss, self).__init__() + super().__init__() self.eps = eps - def forward(self, outputs: Dict, batch: Dict, device: torch.device): + def forward(self, outputs: dict, batch: dict, device: torch.device): predict = outputs["outputs"] targets = move_to(batch["targets"], device) prediction = F.softmax(predict, dim=1) if len(targets.shape) == 3: num_classes = prediction.shape[1] - targets = torch.nn.functional.one_hot( - targets.long(), num_classes=num_classes - ).permute(0, 3, 1, 2) + targets = torch.nn.functional.one_hot(targets.long(), num_classes=num_classes).permute( + 0, 3, 1, 2 + ) # have to use contiguous since they may from a torch.view op iflat = prediction.contiguous().view(-1) diff --git a/theseus/cv/semantic/losses/lovasz_loss.py b/theseus/cv/semantic/losses/lovasz_loss.py index 0384e719..ebc9ac72 100644 --- a/theseus/cv/semantic/losses/lovasz_loss.py +++ b/theseus/cv/semantic/losses/lovasz_loss.py @@ -2,7 +2,7 @@ Lovasz-Softmax and Jaccard hinge loss in PyTorch Maxim Berman 2018 ESAT-PSI KU Leuven (MIT License) """ -from typing import Dict, List + import torch import torch.nn as nn @@ -60,7 +60,7 @@ def lovasz_softmax_flat(prb, lbl, ignore_index, only_present): class LovaszSoftmax(nn.Module): - """ + r""" Multi-class Lovasz-Softmax loss logits: [B, C, H, W] class logits at each prediction (between -\infty and \infty) labels: [B, H, W] Tensor, ground truth labels (between 0 and C - 1) @@ -73,7 +73,7 @@ def __init__(self, ignore_index=None, only_present=True, **kwargs): self.ignore_index = ignore_index self.only_present = only_present - def forward(self, outputs: Dict, batch: Dict, device: torch.device): + def forward(self, outputs: dict, batch: dict, device: torch.device): predict = outputs["outputs"] targets = move_to(batch["targets"], device) @@ -83,9 +83,7 @@ def forward(self, outputs: Dict, batch: Dict, device: torch.device): total_loss = 0 batch_size = predict.shape[0] for prb, lbl in zip(probas, targets): - total_loss += lovasz_softmax_flat( - prb, lbl, self.ignore_index, self.only_present - ) + total_loss += lovasz_softmax_flat(prb, lbl, self.ignore_index, self.only_present) loss = total_loss / batch_size loss_dict = {"LOVASZ": loss.item()} diff --git a/theseus/cv/semantic/losses/tversky_loss.py b/theseus/cv/semantic/losses/tversky_loss.py index 192104ae..354eb172 100644 --- a/theseus/cv/semantic/losses/tversky_loss.py +++ b/theseus/cv/semantic/losses/tversky_loss.py @@ -1,4 +1,3 @@ -from typing import Dict import torch import torch.nn as nn @@ -7,21 +6,21 @@ class FocalTverskyLoss(nn.Module): def __init__(self, smooth=1, alpha=0.7, gamma=0.75, **kwargs): - super(FocalTverskyLoss, self).__init__() + super().__init__() self.smooth = smooth self.alpha = alpha self.beta = 1 - alpha self.gamma = gamma - def forward(self, outputs: Dict, batch: Dict, device: torch.device) -> torch.Tensor: + def forward(self, outputs: dict, batch: dict, device: torch.device) -> torch.Tensor: predict = outputs["outputs"] targets = batch["targets"].to(device) if len(targets.shape) == 3: num_classes = predict.shape[1] - targets = torch.nn.functional.one_hot( - targets.long(), num_classes=num_classes - ).permute(0, 3, 1, 2) + targets = torch.nn.functional.one_hot(targets.long(), num_classes=num_classes).permute( + 0, 3, 1, 2 + ) prediction = F.softmax(predict, dim=1) @@ -34,9 +33,7 @@ def forward(self, outputs: Dict, batch: Dict, device: torch.device) -> torch.Ten FP = ((1 - targets) * prediction).sum() FN = (targets * (1 - prediction)).sum() - tversky = (TP + self.smooth) / ( - TP + self.alpha * FN + self.beta * FP + self.smooth - ) + tversky = (TP + self.smooth) / (TP + self.alpha * FN + self.beta * FP + self.smooth) loss = (1 - tversky) ** self.gamma loss_dict = {"FT": loss.item()} diff --git a/theseus/cv/semantic/metrics/dicecoeff.py b/theseus/cv/semantic/metrics/dicecoeff.py index 5e378611..e2e03a7c 100644 --- a/theseus/cv/semantic/metrics/dicecoeff.py +++ b/theseus/cv/semantic/metrics/dicecoeff.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, Optional +from typing import Any import numpy as np import torch @@ -16,7 +16,7 @@ def __init__(self, num_classes: int, calc_each_class: bool = False, **kwawrgs): self.num_classes = num_classes self.reset() - def update(self, outputs: Dict[str, Any], batch: Dict[str, Any]): + def update(self, outputs: dict[str, Any], batch: dict[str, Any]): """ Perform calculation based on prediction and targets """ @@ -68,9 +68,7 @@ def reset(self): self.sample_size = 0 def value(self): - scores_each_class = ( - self.scores_list / self.sample_size - ) # mean over number of samples + scores_each_class = self.scores_list / self.sample_size # mean over number of samples scores = sum(scores_each_class) / (self.num_classes - 1) # subtract background if self.calc_each_class: diff --git a/theseus/cv/semantic/metrics/miou.py b/theseus/cv/semantic/metrics/miou.py index e8a6b4cf..17ee6d47 100644 --- a/theseus/cv/semantic/metrics/miou.py +++ b/theseus/cv/semantic/metrics/miou.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, Optional +from typing import Any import numpy as np import torch @@ -15,7 +15,7 @@ def __init__(self, num_classes: int = 4, **kwawrgs): self.num_classes = num_classes self.reset() - def update(self, outputs: Dict[str, Any], batch: Dict[str, Any]): + def update(self, outputs: dict[str, Any], batch: dict[str, Any]): """ Perform calculation based on prediction and targets """ @@ -30,9 +30,7 @@ def update(self, outputs: Dict[str, Any], batch: Dict[str, Any]): targets = targets.permute(3, 0, 1, 2).long().squeeze() preds = torch.from_numpy(outputs).long() - one_hot_predicts = torch.nn.functional.one_hot( - preds.long(), num_classes=self.num_classes - ) + one_hot_predicts = torch.nn.functional.one_hot(preds.long(), num_classes=self.num_classes) one_hot_predicts = one_hot_predicts.permute(0, 3, 1, 2) @@ -68,8 +66,6 @@ def reset(self): self.sample_size = 0 def value(self): - scores_each_class = ( - self.scores_list / self.sample_size - ) # mean over number of samples + scores_each_class = self.scores_list / self.sample_size # mean over number of samples scores = sum(scores_each_class) / (self.num_classes - 1) # subtract background return {"miou": scores} diff --git a/theseus/cv/semantic/metrics/pixel_accuracy.py b/theseus/cv/semantic/metrics/pixel_accuracy.py index 2315b81e..377624e2 100644 --- a/theseus/cv/semantic/metrics/pixel_accuracy.py +++ b/theseus/cv/semantic/metrics/pixel_accuracy.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, Optional +from typing import Any import numpy as np import torch @@ -19,10 +19,10 @@ class PixelAccuracy(Metric): def __init__( self, num_classes: int, - thresh: Optional[float] = None, + thresh: float | None = None, eps: float = 1e-6, - ignore_index: Optional[int] = None, - **kwargs + ignore_index: int | None = None, + **kwargs, ): self.thresh = thresh @@ -32,16 +32,14 @@ def __init__( self.eps = eps if self.pred_type == "binary": - assert ( - thresh is not None - ), "Threshold should be specified for binary segmentation" + assert thresh is not None, "Threshold should be specified for binary segmentation" if num_classes == 1: self.num_classes += 1 self.reset() - def update(self, outputs: Dict[str, Any], batch: Dict[str, Any]): + def update(self, outputs: dict[str, Any], batch: dict[str, Any]): """ Perform calculation based on prediction and targets """ @@ -91,12 +89,8 @@ def reset(self): self.sample_size = 0 def value(self): - precision_each_class = ( - self.precisions / self.sample_size - ) # mean over number of samples - recall_each_class = ( - self.recalls / self.sample_size - ) # mean over number of samples + precision_each_class = self.precisions / self.sample_size # mean over number of samples + recall_each_class = self.recalls / self.sample_size # mean over number of samples # Mean over classes if self.pred_type == "binary": diff --git a/theseus/cv/semantic/models/segmodels.py b/theseus/cv/semantic/models/segmodels.py index eeee4787..d3c83409 100644 --- a/theseus/cv/semantic/models/segmodels.py +++ b/theseus/cv/semantic/models/segmodels.py @@ -1,4 +1,4 @@ -from typing import Any, Dict +from typing import Any import segmentation_models_pytorch as smp import torch @@ -29,10 +29,10 @@ def __init__( model_name: str, encoder_name: str = "resnet34", num_classes: int = 1000, - aux_params: Dict = None, + aux_params: dict = None, in_channels: int = 3, pretrained: bool = True, - **kwargs + **kwargs, ): super().__init__() @@ -57,7 +57,7 @@ def get_model(self): """ return self.model - def forward_batch(self, batch: Dict, device: torch.device = None): + def forward_batch(self, batch: dict, device: torch.device = None): if device is not None: x = move_to(batch["inputs"], device) else: @@ -67,7 +67,7 @@ def forward_batch(self, batch: Dict, device: torch.device = None): "outputs": outputs, } - def get_prediction(self, adict: Dict[str, Any], device: torch.device = None): + def get_prediction(self, adict: dict[str, Any], device: torch.device = None): """ Inference using the model. adict: `Dict[str, Any]` diff --git a/theseus/cv/semantic/pipeline.py b/theseus/cv/semantic/pipeline.py index 8d8bf657..47f44244 100644 --- a/theseus/cv/semantic/pipeline.py +++ b/theseus/cv/semantic/pipeline.py @@ -15,7 +15,7 @@ class SemanticPipeline(BasePipeline): """docstring for Pipeline.""" def __init__(self, opt: DictConfig): - super(SemanticPipeline, self).__init__(opt) + super().__init__(opt) self.opt = opt def init_registry(self): diff --git a/theseus/ml/__init__.py b/theseus/ml/__init__.py index e0fc63ea..69b45325 100644 --- a/theseus/ml/__init__.py +++ b/theseus/ml/__init__.py @@ -1 +1,14 @@ from .preprocessors import * +from .tradml import ( + TradMLTuner, + fit_adaboost, + fit_catboost, + fit_knn, + fit_lightgbm, + fit_logistics, + fit_mlp, + fit_rf, + fit_svm, + fit_xgboost, + objective, +) diff --git a/theseus/ml/callbacks/__init__.py b/theseus/ml/callbacks/__init__.py deleted file mode 100644 index 33c6032e..00000000 --- a/theseus/ml/callbacks/__init__.py +++ /dev/null @@ -1,22 +0,0 @@ -from theseus.registry import Registry - -from .base_callbacks import Callbacks, CallbacksList -from .checkpoint_callbacks import SKLearnCheckpointCallbacks -from .explainer import ( - LIMEExplainer, - PartialDependencePlots, - PermutationImportance, - ShapValueExplainer, -) -from .metric_callbacks import MetricLoggerCallbacks -from .optuna_callbacks import OptunaCallbacks - -CALLBACKS_REGISTRY = Registry("CALLBACKS") - -CALLBACKS_REGISTRY.register(SKLearnCheckpointCallbacks) -CALLBACKS_REGISTRY.register(ShapValueExplainer) -CALLBACKS_REGISTRY.register(PermutationImportance) -CALLBACKS_REGISTRY.register(PartialDependencePlots) -CALLBACKS_REGISTRY.register(LIMEExplainer) -CALLBACKS_REGISTRY.register(OptunaCallbacks) -CALLBACKS_REGISTRY.register(MetricLoggerCallbacks) diff --git a/theseus/ml/callbacks/base_callbacks.py b/theseus/ml/callbacks/base_callbacks.py deleted file mode 100644 index bbb6b770..00000000 --- a/theseus/ml/callbacks/base_callbacks.py +++ /dev/null @@ -1,184 +0,0 @@ -# Modified from YOLOv5 🚀 by Ultralytics, GPL-3.0 license - -from typing import Any, Dict, List - -from tabulate import tabulate - -from theseus.base.utilities.loggers.observer import LoggerObserver - -LOGGER = LoggerObserver.getLogger("main") - -hook_names = [ - "sanitycheck", - "on_start", - "on_finish", - "on_epoch_start", - "on_epoch_end", - "on_train_epoch_start", - "on_train_epoch_end", - "on_train_batch_start", - "on_train_batch_end", - "on_train_step", - "on_validation_epoch_start", - "on_validation_epoch_end", - "on_validation_batch_start", - "on_validation_batch_end", - "on_validation_step", -] - - -class Callbacks: - """ - Abstract class for callbacks - """ - - def __init__(self) -> None: - - # Define the available callbacks - self._hooks = {k: None for k in hook_names} - - self.name = self.__class__.__name__ - self.params = None - self.self_register() - - def set_params(self, params): - self.params = params - - def _do_register(self, name: str, func: Any, overide: bool = False) -> None: - assert ( - name in self._hooks.keys() - ), f"Method named '{name}' cannot be used as hook in {self.name}" - - assert ( - self._hooks[name] is None or overide - ), f"""A hook named '{name}' has already been registered in {self._name}. - Please specify `overwrite=True` or use another name""" - - self._hooks[name] = func - - def self_register(self): - for func_name in dir(self): - func = getattr(self, func_name) - if callable(func): - if func_name in self._hooks.keys(): - self.register_hook(func) - - def register_hook( - self, func: Any = None, prefix: str = "", overide: bool = False - ) -> Any: - """ - Register the given object under the the name `obj.__name__`. - Can be used as either a decorator or not. See docstring of this class for usage. - """ - - if func is None: - # used as a decorator - def deco(func_or_class: Any) -> Any: - name = func_or_class._name - self._do_register(prefix + name, func_or_class, overide=overide) - return func_or_class - - return deco - - # used as a function call - name = func.__name__ - self._do_register(prefix + name, func, overide=overide) - - def get(self, name: str) -> Any: - ret = self._hooks.get(name) - if ret is None: - raise KeyError( - "Hook named '{}' has not been registered in '{}'!".format( - name, self._name - ) - ) - return ret - - def __repr__(self) -> str: - table_headers = ["Names", "Objects"] - table = tabulate( - self._hooks.items(), headers=table_headers, tablefmt="fancy_grid" - ) - return "Hook functions of {}:\n".format(self._name) + table - - # pyre-fixme[4]: Attribute must be annotated. - __str__ = __repr__ - - -class CallbacksList: - """ " - Handles all registered callbacks for Hooks - """ - - def __init__(self, callbacks: List[Callbacks]): - - # Define the available callbacks - self._callbacks = {k: [] for k in hook_names} - self._registered_callback_names = [] - self._registered_callbacks = [] - self.params = None - # self.stop_training = False # set True to interrupt training - self.register_callbacks(callbacks) - - def set_params(self, params): - for item in self._registered_callbacks: - item.set_params(params) - - def register_callbacks(self, callbacks: List[Callbacks]): - """ - Register list of callbacks - """ - # Register all callbacks - for callback in callbacks: - if callback.name not in self._registered_callback_names: - for method_name, method_call in callback._hooks.items(): - if method_call is not None: - self.register_action( - method_name, - name=".".join([callback.name, method_name]), - callback=method_call, - ) - self._registered_callback_names.append(callback.name) - self._registered_callbacks.append(callback) - else: - print(f"Duplicate callback named {callback.name} found.") - - def register_action(self, hook, name="", callback=None): - """ - Register a new action to a callback hook - Args: - hook The callback hook name to register the action to - name The name of the action for later reference - callback The callback to fire - """ - assert ( - hook in self._callbacks - ), f"hook '{hook}' not found in callbacks {self._callbacks}" - assert callable(callback), f"callback '{callback}' is not callable" - self._callbacks[hook].append({"name": name, "callback": callback}) - - def get_registered_actions(self, hook=None): - """ " - Returns all the registered actions by callback hook - Args: - hook The name of the hook to check, defaults to all - """ - if hook: - return self._callbacks[hook] - - return self._callbacks - - def run(self, hook, params: Dict = None): - """ - Loop through the registered actions and fire all callbacks - Args: - hook The name of the hook to check, defaults to all - params: dict with parameters - """ - - assert ( - hook in self._callbacks.keys() - ), f"hook {hook} not found in callbacks in {self._callbacks.keys()}" - - for logger in self._callbacks[hook]: - logger["callback"](logs=params) diff --git a/theseus/ml/callbacks/checkpoint_callbacks.py b/theseus/ml/callbacks/checkpoint_callbacks.py deleted file mode 100644 index 2e711e3b..00000000 --- a/theseus/ml/callbacks/checkpoint_callbacks.py +++ /dev/null @@ -1,44 +0,0 @@ -import os -import os.path as osp -from typing import Dict - -from theseus.base.utilities.loggers.observer import LoggerObserver -from theseus.ml.callbacks import Callbacks - -LOGGER = LoggerObserver.getLogger("main") - - -class SKLearnCheckpointCallbacks(Callbacks): - """ - Callbacks for saving checkpoints. - """ - - def __init__( - self, - save_dir: str = "runs", - **kwargs, - ) -> None: - super().__init__() - - self.best_value = 0 - self.save_dir = osp.join(save_dir, "checkpoints") - os.makedirs(self.save_dir, exist_ok=True) - - def save_checkpoint(self, trainer, outname="last"): - """ - Save all information of the current iteration - """ - save_path = osp.join(self.save_dir, outname) - trainer.model.save_model(savepath=save_path) - LOGGER.text( - f"Save model to last.pth", - LoggerObserver.INFO, - ) - - def on_train_epoch_end(self, logs: Dict = None): - """ - On training batch (iteration) end - """ - - # Saving checkpoint - self.save_checkpoint(self.params["trainer"]) diff --git a/theseus/ml/callbacks/explainer/__init__.py b/theseus/ml/callbacks/explainer/__init__.py deleted file mode 100644 index d5d9bd25..00000000 --- a/theseus/ml/callbacks/explainer/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from .lime import LIMEExplainer -from .pdp import PartialDependencePlots -from .permutation import PermutationImportance -from .shapley import ShapValueExplainer diff --git a/theseus/ml/callbacks/explainer/lime.py b/theseus/ml/callbacks/explainer/lime.py deleted file mode 100644 index ddfccf88..00000000 --- a/theseus/ml/callbacks/explainer/lime.py +++ /dev/null @@ -1,76 +0,0 @@ -import os.path as osp -import random -from typing import Dict, List - -from lime import lime_tabular - -from theseus.base.utilities.loggers.observer import LoggerObserver -from theseus.ml.callbacks import Callbacks - -LOGGER = LoggerObserver.getLogger("main") - - -class LIMEExplainer(Callbacks): - def __init__(self, save_dir, **kwargs) -> None: - super().__init__() - self.save_dir = save_dir - - def explain_instance( - self, training_data, model, item, feature_names=None, class_names=None - ): - """ - Get explaination for a single instance - """ - self.explainer = lime_tabular.LimeTabularExplainer( - training_data=training_data, - feature_names=feature_names, - class_names=class_names, - mode="classification" if class_names is not None else "regression", - discretize_continuous=False, - ) - - return self.explainer.explain_instance( - data_row=item, predict_fn=model.predict_proba - ) - - def on_validation_epoch_end(self, logs: Dict = None): - """ - After finish validation - """ - - model = self.params["trainer"].model.get_model() - x_train, y_train = ( - logs["trainset"]["inputs"], - logs["trainset"]["targets"], - ) - x_val, y_val = logs["valset"]["inputs"], logs["valset"]["targets"] - feature_names = logs["valset"]["feature_names"] - classnames = logs["valset"]["classnames"] - - item_id = random.choice(range(len(x_val))) - item = x_val[item_id] - exp = self.explain_instance( - x_train, - model, - item, - feature_names=feature_names, - class_names=classnames, - ) - - save_path = osp.join(self.save_dir, f"lime_{item_id}.html") - exp.save_to_file(save_path) - - LOGGER.log( - [ - { - "tag": f"Importance/LIME/{item_id}", - "value": save_path, - "type": LoggerObserver.HTML, - } - ] - ) - - LOGGER.text( - f"LIME figure for a random instance saved at {save_path}", - level=LoggerObserver.INFO, - ) diff --git a/theseus/ml/callbacks/explainer/pdp.py b/theseus/ml/callbacks/explainer/pdp.py deleted file mode 100644 index 59406365..00000000 --- a/theseus/ml/callbacks/explainer/pdp.py +++ /dev/null @@ -1,119 +0,0 @@ -import os.path as osp -from typing import Dict, List - -import matplotlib.pyplot as plt -from sklearn.inspection import PartialDependenceDisplay, partial_dependence - -from theseus.base.utilities.loggers.observer import LoggerObserver -from theseus.ml.callbacks import Callbacks - -LOGGER = LoggerObserver.getLogger("main") - - -class PartialDependencePlots(Callbacks): - def __init__( - self, - save_dir, - feature_names, - target_name, - kind="both", - num_jobs=1, - num_samples=50, - figsize=(10, 12), - **kwargs, - ) -> None: - - super().__init__() - self.feature_names = feature_names - self.kind = kind - self.save_dir = save_dir - self.num_jobs = num_jobs - self.num_samples = num_samples - self.figsize = figsize - self.target_name = target_name - - self.num_cols = int(len(feature_names) / 3) + 1 - self.num_rows = int(len(feature_names) / self.num_cols) - - def on_train_epoch_end(self, logs: Dict = None): - """ - After finish training - """ - model = self.params["trainer"].model.get_model() - x_train, y_train = ( - logs["trainset"]["inputs"], - logs["trainset"]["targets"], - ) - all_feature_names = logs["trainset"]["feature_names"] - - fig, ax = plt.subplots(self.num_rows, self.num_cols, figsize=self.figsize) - PartialDependenceDisplay.from_estimator( - model, - x_train, - self.feature_names, - feature_names=all_feature_names, - target=self.target_name, - ax=ax, - n_jobs=self.num_jobs, - n_cols=self.num_cols, - subsample=self.num_samples, - ) - fig.suptitle("Partial Dependence Plots") - fig.tight_layout() - - LOGGER.log( - [ - { - "tag": "Importance/PDP/train", - "value": fig, - "type": LoggerObserver.FIGURE, - "kwargs": {"step": 0}, - } - ] - ) - - LOGGER.text( - f"PDP figure saved", - level=LoggerObserver.INFO, - ) - plt.clf() - - def on_validation_epoch_end(self, logs: Dict = None): - """ - After finish validation - """ - model = self.params["trainer"].model.get_model() - x_val, y_val = logs["valset"]["inputs"], logs["valset"]["targets"] - all_feature_names = logs["valset"]["feature_names"] - - fig, ax = plt.subplots(self.num_rows, self.num_cols, figsize=self.figsize) - PartialDependenceDisplay.from_estimator( - model, - x_val, - self.feature_names, - feature_names=all_feature_names, - target=self.target_name, - ax=ax, - n_jobs=self.num_jobs, - n_cols=self.num_cols, - subsample=self.num_samples, - ) - fig.suptitle("Partial Dependence Plots") - fig.tight_layout() - - LOGGER.log( - [ - { - "tag": "Importance/PDP/val", - "value": fig, - "type": LoggerObserver.FIGURE, - "kwargs": {"step": 0}, - } - ] - ) - - LOGGER.text( - f"PDP figure saved", - level=LoggerObserver.INFO, - ) - plt.clf() diff --git a/theseus/ml/callbacks/explainer/permutation.py b/theseus/ml/callbacks/explainer/permutation.py deleted file mode 100644 index e9d4c2b7..00000000 --- a/theseus/ml/callbacks/explainer/permutation.py +++ /dev/null @@ -1,101 +0,0 @@ -import os.path as osp -from typing import Dict, List - -import matplotlib.pyplot as plt -import plotly.graph_objects as go -from sklearn.inspection import permutation_importance - -from theseus.base.utilities.loggers.observer import LoggerObserver -from theseus.ml.callbacks import Callbacks - -LOGGER = LoggerObserver.getLogger("main") - - -class PermutationImportance(Callbacks): - def __init__(self, save_dir, **kwargs) -> None: - super().__init__() - self.explainer = None - self.save_dir = save_dir - - def on_train_epoch_end(self, logs: Dict = None): - """ - After finish training - """ - model = self.params["trainer"].model.get_model() - x_train, y_train = ( - logs["trainset"]["inputs"], - logs["trainset"]["targets"], - ) - feature_names = logs["trainset"]["feature_names"] - classnames = logs["trainset"]["classnames"] - - perm_importance = permutation_importance(model, x_train, y_train) - sorted_idx = perm_importance.importances_mean.argsort() - - fig = go.Figure( - go.Bar( - x=perm_importance.importances_mean[sorted_idx], - y=feature_names[sorted_idx], - orientation="h", - ) - ) - - plt.xlabel("Permutation Importance") - save_path = osp.join(self.save_dir, "permutation_train.html") - fig.write_html(save_path, auto_play=False) - - LOGGER.log( - [ - { - "tag": f"Importance/permutation/train", - "value": save_path, - "type": LoggerObserver.HTML, - } - ] - ) - - LOGGER.text( - f"Permutation figure saved at {save_path}", - level=LoggerObserver.INFO, - ) - plt.clf() - - def on_validation_epoch_end(self, logs: Dict = None): - """ - After finish validation - """ - model = self.params["trainer"].model.get_model() - x_val, y_val = logs["valset"]["inputs"], logs["valset"]["targets"] - feature_names = logs["valset"]["feature_names"] - classnames = logs["valset"]["classnames"] - - perm_importance = permutation_importance(model, x_val, y_val) - sorted_idx = perm_importance.importances_mean.argsort() - - fig = go.Figure( - go.Bar( - x=perm_importance.importances_mean[sorted_idx], - y=feature_names[sorted_idx], - orientation="h", - ) - ) - - plt.xlabel("Permutation Importance") - save_path = osp.join(self.save_dir, "permutation_val.html") - fig.write_html(save_path, auto_play=False) - - LOGGER.log( - [ - { - "tag": f"Importance/permutation/val", - "value": save_path, - "type": LoggerObserver.HTML, - } - ] - ) - - LOGGER.text( - f"Permutation figure saved at {save_path}", - level=LoggerObserver.INFO, - ) - plt.clf() diff --git a/theseus/ml/callbacks/explainer/shapley.py b/theseus/ml/callbacks/explainer/shapley.py deleted file mode 100644 index efb50ba4..00000000 --- a/theseus/ml/callbacks/explainer/shapley.py +++ /dev/null @@ -1,105 +0,0 @@ -import os -import os.path as osp -from typing import Dict, List - -import matplotlib.pyplot as plt -import shap -from sklearn.inspection import permutation_importance - -from theseus.base.utilities.loggers.observer import LoggerObserver -from theseus.ml.callbacks import Callbacks - -LOGGER = LoggerObserver.getLogger("main") - - -class ShapValueExplainer(Callbacks): - def __init__( - self, save_dir, plot_type="bar", check_additivity=True, **kwargs - ) -> None: - super().__init__() - self.plot_type = plot_type - self.explainer = None - self.save_dir = save_dir - self.check_additivity = check_additivity - - def on_train_epoch_end(self, logs: Dict = None): - """ - After finish training - """ - model = self.params["trainer"].model.get_model() - self.explainer = shap.TreeExplainer(model) - x_train, y_train = ( - logs["trainset"]["inputs"], - logs["trainset"]["targets"], - ) - feature_names = logs["trainset"]["feature_names"] - classnames = logs["trainset"]["classnames"] - shap_values = self.explainer.shap_values( - x_train, check_additivity=self.check_additivity - ) - shap.summary_plot( - shap_values, - plot_type=self.plot_type, - feature_names=feature_names, - class_names=classnames, - show=False, - ) - - fig = plt.gcf() - - LOGGER.log( - [ - { - "tag": "Importance/SHAP/train", - "value": fig, - "type": LoggerObserver.FIGURE, - "kwargs": {"step": 0}, - } - ] - ) - - LOGGER.text( - f"Shapley figure saved", - level=LoggerObserver.INFO, - ) - plt.clf() - - def on_validation_epoch_end(self, logs: Dict = None): - """ - After finish validation - """ - model = self.params["trainer"].model.get_model() - self.explainer = shap.TreeExplainer(model) - x_val, y_val = logs["valset"]["inputs"], logs["valset"]["targets"] - feature_names = logs["valset"]["feature_names"] - classnames = logs["valset"]["classnames"] - shap_values = self.explainer.shap_values( - x_val, check_additivity=self.check_additivity - ) - plt.clf() - shap.summary_plot( - shap_values, - plot_type=self.plot_type, - feature_names=feature_names, - class_names=classnames, - show=False, - ) - - fig = plt.gcf() - - LOGGER.log( - [ - { - "tag": "Importance/SHAP/val", - "value": fig, - "type": LoggerObserver.FIGURE, - "kwargs": {"step": 0}, - } - ] - ) - - LOGGER.text( - f"Shapley figure saved", - level=LoggerObserver.INFO, - ) - plt.clf() diff --git a/theseus/ml/callbacks/metric_callbacks.py b/theseus/ml/callbacks/metric_callbacks.py deleted file mode 100644 index dd49c275..00000000 --- a/theseus/ml/callbacks/metric_callbacks.py +++ /dev/null @@ -1,72 +0,0 @@ -import json -import os -import os.path as osp -from typing import Dict, List - -from theseus.base.utilities.loggers.observer import LoggerObserver -from theseus.ml.callbacks import Callbacks - -LOGGER = LoggerObserver.getLogger("main") - - -class MetricLoggerCallbacks(Callbacks): - """ - Callbacks for logging running metric while training every epoch end - Features: - - Only do logging - """ - - def __init__(self, save_json: bool = True, **kwargs) -> None: - super().__init__() - self.save_json = save_json - if self.save_json: - self.save_dir = kwargs.get("save_dir", None) - if self.save_dir is not None: - self.save_dir = osp.join(self.save_dir, "Validation") - os.makedirs(self.save_dir, exist_ok=True) - self.output_dict = [] - - def on_validation_epoch_end(self, logs: Dict = None): - """ - After finish validation - """ - - iters = logs["iters"] - metric_dict = logs["metric_dict"] - - # Save json - if self.save_json: - item = {} - for metric, score in metric_dict.items(): - if isinstance(score, (int, float)): - item[metric] = float(f"{score:.5f}") - if len(item.keys()) > 0: - item["iters"] = iters - self.output_dict.append(item) - - # Log metric - metric_string = "" - for metric, score in metric_dict.items(): - if isinstance(score, (int, float)): - metric_string += metric + ": " + f"{score:.5f}" + " | " - metric_string += "\n" - - LOGGER.text(metric_string, level=LoggerObserver.INFO) - - # Call other loggers - log_dict = [ - {"tag": f"Validation/{k}", "value": v, "kwargs": {"step": iters}} - for k, v in metric_dict.items() - ] - - LOGGER.log(log_dict) - - def on_finish(self, logs: Dict = None): - """ - After finish everything - """ - if self.save_json: - save_json = osp.join(self.save_dir, "metrics.json") - if len(self.output_dict) > 0: - with open(save_json, "w") as f: - json.dump(self.output_dict, f) diff --git a/theseus/ml/callbacks/optuna_callbacks.py b/theseus/ml/callbacks/optuna_callbacks.py deleted file mode 100644 index 16f284a4..00000000 --- a/theseus/ml/callbacks/optuna_callbacks.py +++ /dev/null @@ -1,35 +0,0 @@ -from typing import Dict, List - -import optuna - -from theseus.base.utilities.loggers.observer import LoggerObserver -from theseus.ml.callbacks import Callbacks - -LOGGER = LoggerObserver.getLogger("main") - - -class OptunaCallbacks(Callbacks): - """ - Callbacks for reporting value to optuna trials to decide whether to prune - """ - - def __init__(self, trial: optuna.Trial, **kwargs) -> None: - super().__init__() - self.trial = trial - - def on_validation_epoch_end(self, logs: Dict = None): - """ - After finish validation - """ - - iters = logs["iters"] - metric_dict = logs["metric_dict"] - - best_key = self.trial.user_attrs["best_key"] - self.trial.report(value=metric_dict[best_key], step=iters) - - if self.trial.should_prune(): - LOGGER.text( - f"Trial {self.trial.number} has been pruned", level=LoggerObserver.DEBUG - ) - raise optuna.TrialPruned() diff --git a/theseus/ml/datasets/__init__.py b/theseus/ml/datasets/__init__.py deleted file mode 100644 index a4b4d1a6..00000000 --- a/theseus/ml/datasets/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from theseus.base.datasets import DATALOADER_REGISTRY, DATASET_REGISTRY - -from .csv_dataset import TabularCSVDataset - -DATASET_REGISTRY.register(TabularCSVDataset) diff --git a/theseus/ml/datasets/csv_dataset.py b/theseus/ml/datasets/csv_dataset.py deleted file mode 100644 index 3c60902d..00000000 --- a/theseus/ml/datasets/csv_dataset.py +++ /dev/null @@ -1,33 +0,0 @@ -import pandas as pd - - -class TabularCSVDataset: - def __init__( - self, data_path, target_column, txt_classnames=None, transform=None - ) -> None: - self.data_path = data_path - self.transform = transform - self.target_column = target_column - self.txt_classnames = txt_classnames - - if self.txt_classnames is not None: - self.classnames = open(self.txt_classnames, "r").read().splitlines() - else: - self.classnames = None - - def load_data(self): - df = pd.read_csv(self.data_path) - if self.transform is not None: - df = self.transform.run(df) - (X, y) = ( - df.drop(self.target_column, axis=1).values, - df[self.target_column].values, - ) - - return { - "inputs": X, - "targets": y, - "feature_names": df.drop(self.target_column, axis=1).columns, - "classnames": self.classnames, - "target_name": self.target_column, - } diff --git a/theseus/ml/metrics/__init__.py b/theseus/ml/metrics/__init__.py deleted file mode 100644 index c3965381..00000000 --- a/theseus/ml/metrics/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -from theseus.base.metrics import METRIC_REGISTRY - -from .acccuracy import SKLAccuracy, SKLBalancedAccuracyMetric -from .confusion_matrix import SKLConfusionMatrix -from .f1_score import SKLF1ScoreMetric -from .mcc import SKLMCC -from .precision_recall import SKLPrecisionRecall -from .projection import SKLEmbeddingProjection -from .roc_auc_score import SKLROCAUCScore - -METRIC_REGISTRY.register(SKLPrecisionRecall) -METRIC_REGISTRY.register(SKLF1ScoreMetric) -METRIC_REGISTRY.register(SKLAccuracy) -METRIC_REGISTRY.register(SKLBalancedAccuracyMetric) -METRIC_REGISTRY.register(SKLEmbeddingProjection) -METRIC_REGISTRY.register(SKLMCC) -METRIC_REGISTRY.register(SKLROCAUCScore) -METRIC_REGISTRY.register(SKLConfusionMatrix) diff --git a/theseus/ml/metrics/acccuracy.py b/theseus/ml/metrics/acccuracy.py deleted file mode 100644 index 558f59e4..00000000 --- a/theseus/ml/metrics/acccuracy.py +++ /dev/null @@ -1,60 +0,0 @@ -from typing import Any, Dict - -import numpy as np -from scipy.special import softmax -from sklearn.metrics import balanced_accuracy_score - -from theseus.base.metrics.metric_template import Metric - - -class SKLAccuracy(Metric): - """ - Accuracy metric - """ - - def __init__(self, **kwargs): - super().__init__(**kwargs) - - def value(self, output: Dict[str, Any], batch: Dict[str, Any]): - """ - Perform calculation based on prediction and targets - """ - output = output["outputs"] - target = batch["targets"] - - probs = softmax(output, axis=-1) - predictions = np.argmax(probs, axis=-1) - - correct = (predictions.reshape(-1) == target.reshape(-1)).sum() - score = correct * 1.0 / target.shape[0] - return {"acc": score} - - -def compute_multiclass(outputs, targets, index): - correct = 0 - sample_size = 0 - for i, j in zip(outputs, targets): - if j == index: - sample_size += 1 - if i == j: - correct += 1 - return correct, sample_size - - -class SKLBalancedAccuracyMetric(Metric): - """ - Balanced Accuracy metric for classification - """ - - def __init__(self, **kwargs): - super().__init__(**kwargs) - - def value(self, outputs: Dict[str, Any], batch: Dict[str, Any]): - - outputs = outputs["outputs"] - targets = batch["targets"] - predictions = np.argmax(outputs, axis=-1).reshape(-1).tolist() - targets = targets.reshape(-1).tolist() - blacc_score = balanced_accuracy_score(targets, predictions) - - return {"bl_acc": blacc_score} diff --git a/theseus/ml/metrics/confusion_matrix.py b/theseus/ml/metrics/confusion_matrix.py deleted file mode 100644 index bc1d769d..00000000 --- a/theseus/ml/metrics/confusion_matrix.py +++ /dev/null @@ -1,98 +0,0 @@ -from typing import Any, Dict, List, Optional - -import matplotlib.pyplot as plt -import numpy as np -import seaborn as sns -from scipy.special import softmax -from sklearn.metrics import confusion_matrix, multilabel_confusion_matrix - -from theseus.base.metrics.metric_template import Metric - - -def plot_cfm(cm, ax, labels: List): - """ - Make confusion matrix figure - labels: `Optional[List]` - classnames for visualization - """ - - ax = sns.heatmap(cm, annot=False, fmt="", cmap="Blues", ax=ax) - - ax.set_xlabel("\nActual") - ax.set_ylabel("Predicted ") - - ax.xaxis.set_ticklabels(labels) - ax.yaxis.set_ticklabels(labels, rotation=0) - - -def make_cm_fig(cms, labels: Optional[List] = None): - - if cms.shape[0] > 1: # multilabel - num_classes = cms.shape[0] - else: - num_classes = cms.shape[1] - - ## Ticket labels - List must be in alphabetical order - if not labels: - labels = [str(i) for i in range(num_classes)] - - ## - num_cfms = cms.shape[0] - nrow = int(np.ceil(np.sqrt(num_cfms))) - - # Clear figures first to prevent memory-consuming - plt.cla() - plt.clf() - plt.close() - - fig, axes = plt.subplots(nrow, nrow, figsize=(8, 8)) - - if num_cfms > 1: - for ax, cfs_matrix, label in zip(axes.flatten(), cms, labels): - ax.set_title(f"{label}\n\n") - plot_cfm(cfs_matrix, ax, labels=["N", "Y"]) - else: - plot_cfm(cms[0], axes, labels=labels) - - fig.tight_layout() - return fig - - -class SKLConfusionMatrix(Metric): - """ - Confusion Matrix metric for classification - """ - - def __init__(self, classnames=None, label_type: str = "multiclass", **kwargs): - super().__init__(**kwargs) - self.type = label_type - self.classnames = classnames - self.num_classes = ( - [i for i in range(len(self.classnames))] if classnames is not None else None - ) - - def value(self, output: Dict[str, Any], batch: Dict[str, Any]): - """ - Perform calculation based on prediction and targets - """ - output = output["outputs"] - target = batch["targets"] - - probs = softmax(output, axis=-1) - predictions = np.argmax(probs, axis=-1) - - if self.type == "multiclass": - values = confusion_matrix( - predictions, - target, - labels=self.num_classes, - normalize="pred", - ) - values = values[np.newaxis, :, :] - else: - values = multilabel_confusion_matrix( - predictions, target, labels=self.num_classes - ) - - fig = make_cm_fig(values, self.classnames) - return {"cfm": fig} diff --git a/theseus/ml/metrics/f1_score.py b/theseus/ml/metrics/f1_score.py deleted file mode 100644 index 8301f7b2..00000000 --- a/theseus/ml/metrics/f1_score.py +++ /dev/null @@ -1,29 +0,0 @@ -from typing import Any, Dict - -import numpy as np -from sklearn.metrics import f1_score - -from theseus.base.metrics.metric_template import Metric - - -class SKLF1ScoreMetric(Metric): - """ - F1 Score Metric (including macro, micro) - """ - - def __init__(self, average="weighted", **kwargs): - super().__init__(**kwargs) - self.average = average - - def value(self, outputs: Dict[str, Any], batch: Dict[str, Any]): - """ - Perform calculation based on prediction and targets - """ - targets = batch["targets"] - outputs = outputs["outputs"] - - self.preds = np.argmax(outputs, axis=1).reshape(-1).tolist() - self.targets = targets.reshape(-1).tolist() - - score = f1_score(self.targets, self.preds, average=self.average) - return {f"{self.average}-f1": score} diff --git a/theseus/ml/metrics/mcc.py b/theseus/ml/metrics/mcc.py deleted file mode 100644 index 21a15c99..00000000 --- a/theseus/ml/metrics/mcc.py +++ /dev/null @@ -1,28 +0,0 @@ -from typing import Any, Dict - -import numpy as np -from sklearn.metrics import matthews_corrcoef - -from theseus.base.metrics.metric_template import Metric - - -class SKLMCC(Metric): - """ - Mathew Correlation Coefficient - """ - - def __init__(self, **kwargs): - super().__init__(**kwargs) - - def value(self, outputs: Dict[str, Any], batch: Dict[str, Any]): - """ - Perform calculation based on prediction and targets - """ - targets = batch["targets"] - outputs = outputs["outputs"] - - self.preds = np.argmax(outputs, axis=1).reshape(-1).tolist() - self.targets = targets.reshape(-1).tolist() - - score = matthews_corrcoef(self.targets, self.preds) - return {f"mcc": score} diff --git a/theseus/ml/metrics/precision_recall.py b/theseus/ml/metrics/precision_recall.py deleted file mode 100644 index 1cf49b3e..00000000 --- a/theseus/ml/metrics/precision_recall.py +++ /dev/null @@ -1,39 +0,0 @@ -from typing import Any, Dict - -import numpy as np -from sklearn.metrics import precision_score, recall_score - -from theseus.base.metrics.metric_template import Metric - - -class SKLPrecisionRecall(Metric): - """ - F1 Score Metric (including macro, micro) - """ - - def __init__(self, average="weighted", **kwargs): - super().__init__(**kwargs) - self.average = average - self.targets = [] - self.preds = [] - - def value(self, outputs: Dict[str, Any], batch: Dict[str, Any]): - """ - Perform calculation based on prediction and targets - """ - targets = batch["targets"] - outputs = outputs["outputs"] - - self.preds += np.argmax(outputs, axis=1).reshape(-1).tolist() - self.targets += targets.reshape(-1).tolist() - - precision = precision_score( - self.targets, self.preds, average=self.average, zero_division=1 - ) - recall = recall_score( - self.targets, self.preds, average=self.average, zero_division=1 - ) - return { - f"{self.average}-precision": precision, - f"{self.average}-recall": recall, - } diff --git a/theseus/ml/metrics/projection.py b/theseus/ml/metrics/projection.py deleted file mode 100644 index 1e2bc64a..00000000 --- a/theseus/ml/metrics/projection.py +++ /dev/null @@ -1,67 +0,0 @@ -import os -from typing import Any, Dict - -import numpy as np -from scipy.special import softmax - -from theseus.base.metrics.metric_template import Metric -from theseus.base.utilities.loggers import LoggerObserver - -LOGGER = LoggerObserver.getLogger("main") - - -class SKLEmbeddingProjection(Metric): - """ - Visualize embedding project for classification - """ - - def __init__(self, classnames=None, save_dir=".cache", has_labels=True, **kwargs): - super().__init__(**kwargs) - self.has_labels = has_labels - self.save_dir = save_dir - self.classnames = classnames - os.makedirs(self.save_dir, exist_ok=True) - - def value(self, outputs: Dict[str, Any], batch: Dict[str, Any]): - """ - Perform calculation based on prediction and targets - """ - - embeddings = batch["inputs"] - targets = batch["targets"] - probs = softmax(outputs["outputs"], axis=-1) - predictions = np.argmax(probs, axis=-1) - - ## Metadata, in column style - if self.has_labels: - if self.classnames is not None: - metadata = [ - (self.classnames[int(a)], self.classnames[int(b)]) - for a, b in zip(targets, predictions) - ] - else: - metadata = [a for a in zip(targets, predictions)] - metadata_header = ["ground truth", "prediction"] - else: - if self.classnames is not None: - metadata = [[self.classnames[int(a)] for a in predictions]] - else: - metadata = [predictions] - metadata_header = ["prediction"] - - LOGGER.log( - [ - { - "tag": f"Projection", - "value": embeddings, - "type": LoggerObserver.EMBED, - "kwargs": { - "step": 0, - "metadata": metadata, - "metadata_header": metadata_header, - }, - } - ] - ) - - return {"projection": "Embedding projection generated"} diff --git a/theseus/ml/metrics/roc_auc_score.py b/theseus/ml/metrics/roc_auc_score.py deleted file mode 100644 index 17b511bc..00000000 --- a/theseus/ml/metrics/roc_auc_score.py +++ /dev/null @@ -1,69 +0,0 @@ -from typing import Any, Dict - -import numpy as np -import scipy - -from theseus.base.metrics.metric_template import Metric - -try: - from scikitplot.metrics import plot_precision_recall_curve, plot_roc_curve - - has_scikitplot = True -except: - has_scikitplot = False -from sklearn.metrics import roc_auc_score - - -class SKLROCAUCScore(Metric): - """ - ROC AUC Score - """ - - def __init__( - self, - average: str = "weighted", - label_type: str = "ovr", - plot_curve: bool = True, - **kwargs, - ): - super().__init__(**kwargs) - self.plot_curve = plot_curve - self.label_type = label_type - self.average = average - assert self.label_type in [ - "raise", - "ovr", - "ovo", - ], "Invalid type for multiclass ROC AUC score" - - def value(self, outputs: Dict[str, Any], batch: Dict[str, Any]): - """ - Perform calculation based on prediction and targets - """ - targets = batch["targets"] - outputs = outputs["outputs"] - - if self.label_type == "ovr": - outputs = scipy.special.softmax(outputs, axis=-1) - - self.preds = outputs.tolist() - self.targets = targets.reshape(-1).tolist() - - roc_auc_scr = roc_auc_score( - self.targets, self.preds, average=self.average, multi_class=self.label_type - ) - results = { - f"{self.average}-roc_auc_score": roc_auc_scr, - } - - if has_scikitplot and self.plot_curve: - roc_curve_fig = plot_roc_curve(self.targets, self.preds).get_figure() - pr_fig = plot_precision_recall_curve(self.targets, self.preds).get_figure() - results.update( - { - "roc_curve": roc_curve_fig, - "precision_recall_curve": pr_fig, - } - ) - - return results diff --git a/theseus/ml/models/__init__.py b/theseus/ml/models/__init__.py deleted file mode 100644 index 0d56831e..00000000 --- a/theseus/ml/models/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from theseus.base.models import MODEL_REGISTRY - -from .gbms import GBClassifiers - -MODEL_REGISTRY.register(GBClassifiers) diff --git a/theseus/ml/models/gbms.py b/theseus/ml/models/gbms.py deleted file mode 100644 index 6c09ff9e..00000000 --- a/theseus/ml/models/gbms.py +++ /dev/null @@ -1,70 +0,0 @@ -import catboost as cb -import lightgbm as lgb -import xgboost as xgb -from omegaconf import DictConfig, OmegaConf - -from theseus.base.utilities.loggers.observer import LoggerObserver - -LOGGER = LoggerObserver.getLogger("main") - - -class GBClassifiers: - def __init__( - self, - model_name, - num_classes, - model_config: DictConfig = {}, - training_params={}, - **kwargs, - ): - OmegaConf.set_struct(model_config, False) - self.training_params = training_params - self.model_name = model_name - self.num_classes = num_classes - if model_name == "catboost": - self.model = cb.CatBoostClassifier(**model_config) - elif model_name == "lightgbm": - model_config.update({"num_class": num_classes}) - self.model = lgb.LGBMClassifier(**model_config) - elif model_name == "xgboost": - model_config.update({"num_class": num_classes}) - self.model = xgb.XGBClassifier(**model_config) - else: - LOGGER.text("Model not supported", level=LoggerObserver.ERROR) - - def get_model(self): - return self.model - - def fit(self, trainset, valset, **kwargs): - X, y = trainset - self.model.fit( - X.copy(), - y.copy(), - eval_set=[trainset, valset], - # eval_set=[(trainset, 'train'), (valset, 'validation')], - **self.training_params, - ) - - def save_model(self, savepath): - if self.model_name == "xgboost": - self.model.save_model(savepath) - elif self.model_name == "lightgbm": - # LightGBM models should be saved as .txt files - self.model.booster_.save_model(savepath) - elif self.model_name == "xgboost": - self.model.save(savepath) - - LOGGER.text(f"Model saved at {savepath}", level=LoggerObserver.INFO) - - def load_model(self, checkpoint_path): - self.model.load_model(checkpoint_path) - LOGGER.text( - f"Loaded checkpoint at {checkpoint_path}", - level=LoggerObserver.INFO, - ) - - def predict(self, X, return_probs=False): - if return_probs: - return self.model.predict_proba(X) - else: - return self.model.predict(X) diff --git a/theseus/ml/pipeline.py b/theseus/ml/pipeline.py deleted file mode 100644 index 6ea1b632..00000000 --- a/theseus/ml/pipeline.py +++ /dev/null @@ -1,191 +0,0 @@ -import os -from datetime import datetime - -from omegaconf import DictConfig, OmegaConf - -from theseus.base.utilities.folder import get_new_folder_name -from theseus.base.utilities.getter import get_instance, get_instance_recursively -from theseus.base.utilities.loggers import FileLogger, ImageWriter, LoggerObserver -from theseus.base.utilities.seed import seed_everything -from theseus.ml.callbacks import CALLBACKS_REGISTRY -from theseus.ml.datasets import DATALOADER_REGISTRY, DATASET_REGISTRY -from theseus.ml.metrics import METRIC_REGISTRY -from theseus.ml.models import MODEL_REGISTRY -from theseus.ml.preprocessors import TRANSFORM_REGISTRY -from theseus.ml.trainer import TRAINER_REGISTRY - - -class MLPipeline(object): - """docstring for Pipeline.""" - - def __init__(self, opt: DictConfig): - self.opt = opt - self.seed = self.opt["global"].get("seed", 1702) - seed_everything(self.seed) - self.initialized = False - - def init_globals(self): - # Main Loggers - self.logger = LoggerObserver.getLogger("main") - - # Global variables - self.exp_name = self.opt["global"].get("exp_name", None) - self.exist_ok = self.opt["global"].get("exist_ok", False) - self.debug = self.opt["global"].get("debug", False) - self.resume = self.opt["global"].get("resume", None) - self.pretrained = self.opt["global"].get("pretrained", None) - self.transform_cfg = self.opt["global"].get("cfg_transform", None) - - # Experiment name - if self.exp_name: - self.savedir = os.path.join( - self.opt["global"].get("save_dir", "runs"), self.exp_name - ) - if not self.exist_ok: - self.savedir = get_new_folder_name(self.savedir) - else: - self.savedir = os.path.join( - self.opt["global"].get("save_dir", "runs"), - datetime.now().strftime("%Y-%m-%d_%H-%M-%S"), - ) - os.makedirs(self.savedir, exist_ok=True) - - # Logging to files - file_logger = FileLogger(__name__, self.savedir, debug=self.debug) - self.logger.subscribe(file_logger) - - # Logging images - image_logger = ImageWriter(self.savedir) - self.logger.subscribe(image_logger) - - self.transform_cfg = self.opt.get("augmentations", None) - - # Logging out configs - self.logger.text("\n" + OmegaConf.to_yaml(self.opt), level=LoggerObserver.INFO) - self.logger.text( - f"Everything will be saved to {self.savedir}", - level=LoggerObserver.INFO, - ) - - def init_registry(self): - self.callbacks_registry = CALLBACKS_REGISTRY - self.transform_registry = TRANSFORM_REGISTRY - self.model_registry = MODEL_REGISTRY - self.metric_registry = METRIC_REGISTRY - self.trainer_registry = TRAINER_REGISTRY - self.dataset_registry = DATASET_REGISTRY - self.dataloader_registry = DATALOADER_REGISTRY - self.logger.text("Overidding registry in pipeline...", LoggerObserver.INFO) - - def init_model(self): - classnames = self.val_dataset["classnames"] - num_classes = len(classnames) - self.model = get_instance( - self.opt["model"], num_classes=num_classes, registry=self.model_registry - ) - - def init_train_dataloader(self): - self.transform = get_instance_recursively( - self.transform_cfg, registry=self.transform_registry - ) - self.train_dataset = get_instance_recursively( - self.opt["data"]["dataset"]["train"], - registry=self.dataset_registry, - transform=self.transform["train"], - ).load_data() - - self.logger.text( - f"Training shape: {self.train_dataset['inputs'].shape}", - level=LoggerObserver.INFO, - ) - - def init_metrics(self): - CLASSNAMES = getattr(self.val_dataset, "classnames", None) - self.metrics = get_instance_recursively( - self.opt["metrics"], - registry=self.metric_registry, - num_classes=len(CLASSNAMES) if CLASSNAMES is not None else None, - classnames=CLASSNAMES, - ) - - def init_callbacks(self): - callbacks = get_instance_recursively( - self.opt["callbacks"], - save_dir=getattr(self, "savedir", "runs"), - resume=getattr(self, "resume", None), - config_dict=self.opt, - registry=self.callbacks_registry, - ) - return callbacks - - def init_validation_dataloader(self): - self.transform = get_instance_recursively( - self.transform_cfg, registry=self.transform_registry - ) - self.val_dataset = get_instance_recursively( - self.opt["data"]["dataset"]["val"], - registry=self.dataset_registry, - transform=self.transform["val"], - ).load_data() - - classnames = self.val_dataset["classnames"] - num_classes = len(classnames) - - self.logger.text( - f"Validation shape: {self.val_dataset['inputs'].shape}", - level=LoggerObserver.INFO, - ) - self.logger.text( - f"Number of classes: {num_classes}", - level=LoggerObserver.INFO, - ) - - def init_trainer(self, callbacks=None): - self.trainer = get_instance( - self.opt["trainer"], - model=self.model, - trainset=getattr(self, "train_dataset", None), - valset=getattr(self, "val_dataset", None), - metrics=self.metrics, - callbacks=callbacks, - registry=self.trainer_registry, - ) - - def init_loading(self): - if getattr(self, "pretrained", None): - self.model.load_model(self.pretrained) - - def init_pipeline(self, train=False): - if self.initialized: - return - self.init_globals() - self.init_registry() - if train: - self.init_train_dataloader() - self.init_validation_dataloader() - self.init_model() - self.init_loading() - self.init_metrics() - callbacks = self.init_callbacks() - self.save_configs() - else: - self.init_validation_dataloader() - self.init_model() - self.init_metrics() - self.init_loading() - callbacks = [] - - self.init_trainer(callbacks=callbacks) - self.initialized = True - - def save_configs(self): - with open(os.path.join(self.savedir, "pipeline.yaml"), "w") as f: - OmegaConf.save(config=self.opt, f=f) - - def fit(self): - self.init_pipeline(train=True) - self.trainer.fit() - - def evaluate(self): - self.init_pipeline(train=False) - return self.trainer.validate() diff --git a/theseus/ml/preprocessors/aggregation.py b/theseus/ml/preprocessors/aggregation.py index b125ca4c..17abf720 100644 --- a/theseus/ml/preprocessors/aggregation.py +++ b/theseus/ml/preprocessors/aggregation.py @@ -32,14 +32,10 @@ def run(self, df): if method_name == "subtract": df[target_name] = df[based_columns].sub(axis=1) if method_name == "concat": - df[target_name] = ( - df[based_columns].astype(str).agg(" ".join, axis=1) - ) + df[target_name] = df[based_columns].astype(str).agg(" ".join, axis=1) elif callable(method_name): - df[target_name] = self.apply( - df[based_columns], function=method_name, axis=1 - ) + df[target_name] = self.apply(df[based_columns], function=method_name, axis=1) else: LOGGER.text( "Unsuppported aggregation method", diff --git a/theseus/ml/preprocessors/base.py b/theseus/ml/preprocessors/base.py index 41b4adac..96d61ea8 100644 --- a/theseus/ml/preprocessors/base.py +++ b/theseus/ml/preprocessors/base.py @@ -15,24 +15,20 @@ use_parallel = True except: use_parallel = False - LOGGER.text( - "pandarallel should be installed for parallerization. Using normal apply-function instead", - level=LoggerObserver.WARN, - ) + # LOGGER.text( + # "pandarallel should be installed for parallerization. Using normal apply-function instead", + # level=LoggerObserver.WARN, + # ) class Preprocessor: - def __init__( - self, column_names=None, exclude_columns=None, verbose=False, **kwargs - ): + def __init__(self, column_names=None, exclude_columns=None, verbose=False, **kwargs): self.verbose = verbose self.column_names = column_names self.filter = None if column_names is not None: - self.filter = FilterColumnNames( - patterns=column_names, excludes=exclude_columns - ) + self.filter = FilterColumnNames(patterns=column_names, excludes=exclude_columns) def apply(self, df, function, parallel=True, axis=0, show_progress=True): diff --git a/theseus/ml/preprocessors/compose.py b/theseus/ml/preprocessors/compose.py index 9f42b859..a5c06876 100644 --- a/theseus/ml/preprocessors/compose.py +++ b/theseus/ml/preprocessors/compose.py @@ -1,10 +1,9 @@ -from typing import List from .base import Preprocessor class PreprocessCompose(Preprocessor): - def __init__(self, preproc_list: List[Preprocessor], **kwargs): + def __init__(self, preproc_list: list[Preprocessor], **kwargs): self.preproc_list = preproc_list def run(self, df): diff --git a/theseus/ml/preprocessors/drop_col.py b/theseus/ml/preprocessors/drop_col.py index 1e2f3510..3d8617d2 100644 --- a/theseus/ml/preprocessors/drop_col.py +++ b/theseus/ml/preprocessors/drop_col.py @@ -46,9 +46,7 @@ def __init__(self, **kwargs): super().__init__(**kwargs) def run(self, df): - cols_to_use = [ - idx for idx, val in (df.isna().mean() >= 1.0).items() if val == False - ] + cols_to_use = [idx for idx, val in (df.isna().mean() >= 1.0).items() if not val] empty_cols = set(df.columns) - set(cols_to_use) df = df.loc[:, cols_to_use] self.log(f"Dropped empty columns: {empty_cols}") diff --git a/theseus/ml/preprocessors/encoder.py b/theseus/ml/preprocessors/encoder.py index cab5ab60..8194d595 100644 --- a/theseus/ml/preprocessors/encoder.py +++ b/theseus/ml/preprocessors/encoder.py @@ -12,9 +12,7 @@ class LabelEncode(Preprocessor): - def __init__( - self, encoder_type="le", pickle_path=None, engine: str = "pandas", **kwargs - ): + def __init__(self, encoder_type="le", pickle_path=None, engine: str = "pandas", **kwargs): super().__init__(**kwargs) assert encoder_type in [ @@ -27,7 +25,7 @@ def __init__( self.pickle_path = pickle_path self.engine = engine if self.engine == "polars": - import polars as pl + pass if self.pickle_path is not None: with open(self.pickle_path, "rb") as fb: @@ -36,18 +34,16 @@ def __init__( self.encoder_type = config["encoder_type"] self.engine = config["engine"] self.encoders = config["encoders"] - self.log(f"Loaded mapping dict from {self.pickle_path}") else: self.encoders = {} - if self.encoder_type == "le": - encoder = LabelEncoder() - elif self.encoder_type == "onehot": - encoder = OneHotEncoder() - else: - encoder = OrdinalEncoder() - - for column in self.column_names: - self.encoders[column] = encoder + if self.column_names is not None: + for column in self.column_names: + if self.encoder_type == "le": + self.encoders[column] = LabelEncoder() + elif self.encoder_type == "onehot": + self.encoders[column] = OneHotEncoder() + else: + self.encoders[column] = OrdinalEncoder() @classmethod def from_pickle(cls, pickle_path: str): @@ -94,7 +90,7 @@ def encode_corpus(self, df): elif self.engine == "polars": import polars as pl - encoder.fit_transform(df[column_name].to_numpy()) + encoder.fit_transform(df.select(column_name).unique().to_numpy().ravel()) le_name_mapping = dict( zip( encoder.classes_, @@ -139,6 +135,16 @@ def run(self, df): level=LoggerObserver.WARN, ) self.column_names = [col for col, dt in df.dtypes.items() if dt == object] + + for column in self.column_names: + if column not in self.encoders: + if self.encoder_type == "le": + self.encoders[column] = LabelEncoder() + elif self.encoder_type == "onehot": + self.encoders[column] = OneHotEncoder() + else: + self.encoders[column] = OrdinalEncoder() + df = self.encode_corpus(df) self.log(f"Label-encoded columns: {self.column_names}") diff --git a/theseus/ml/preprocessors/splitter.py b/theseus/ml/preprocessors/splitter.py index 3a45a308..16f5a96b 100644 --- a/theseus/ml/preprocessors/splitter.py +++ b/theseus/ml/preprocessors/splitter.py @@ -46,9 +46,7 @@ def __init__( elif self.splitter_type == "stratifiedkfold": assert label_column is not None, "Label column should be specified" assert n_splits is not None, "number of splits should be specified" - self.splitter = StratifiedKFold( - n_splits=n_splits, random_state=self.seed, shuffle=True - ) + self.splitter = StratifiedKFold(n_splits=n_splits, random_state=self.seed, shuffle=True) elif self.splitter_type == "default": assert ratio is not None, "should specify ratio" self.ratio = ratio @@ -76,9 +74,7 @@ def run(self, df): elif self.splitter_type == "unique": unique_values = df[self.label_column].unique().tolist() num_unique_samples = len(unique_values) - train_idx = self.splitter( - unique_values, int(num_unique_samples * self.ratio) - ) + train_idx = self.splitter(unique_values, int(num_unique_samples * self.ratio)) train_df = df[df[self.label_column].isin(train_idx)] val_df = df[~df[self.label_column].isin(train_idx)] train_df.to_csv(osp.join(self.save_folder, "train.csv"), index=False) diff --git a/theseus/ml/tradml.py b/theseus/ml/tradml.py new file mode 100644 index 00000000..b924ae8b --- /dev/null +++ b/theseus/ml/tradml.py @@ -0,0 +1,875 @@ +import json +import os +import os.path as osp + +import lightgbm +import numpy as np +import optuna +import wandb +import xgboost as xgb +from optuna.integration.wandb import WeightsAndBiasesCallback +from optuna.storages import JournalFileStorage, JournalStorage +from optuna.visualization import ( + plot_contour, + plot_edf, + plot_intermediate_values, + plot_optimization_history, + plot_parallel_coordinate, + plot_param_importances, + plot_slice, +) +from sklearn.metrics import ( + accuracy_score, + f1_score, + matthews_corrcoef, + precision_score, + recall_score, +) + +from theseus import LoggerObserver +from theseus.ml.visualize import visualize_shap + +LOGGER = LoggerObserver.getLogger("main") + +# wandb might cause an error without this. +os.environ["WANDB_START_METHOD"] = "thread" + +__all__ = [ + "fit_xgboost", + "fit_catboost", + "fit_lightgbm", + "fit_rf", + "fit_svm", + "fit_logistics", + "fit_adaboost", + "fit_knn", + "fit_mlp", + "objective", + "TradMLTuner", +] + + +def fit_xgboost(params, X_train, X_val, y_train, y_val, is_classification=True): + if is_classification: + num_classes = len(np.unique(y_train)) + objective_fn = "multi:softprob" if num_classes > 2 else "binary:logistic" + params.update( + {"eval_metric": ["auc"], "objective": objective_fn, "early_stopping_rounds": 100} + ) + model = xgb.XGBClassifier(**params) + else: + params.update( + { + "eval_metric": ["rmse"], + "objective": "reg:squarederror", + } + ) + model = xgb.XGBRegressor(**params) + model.fit(X_train, y_train, eval_set=[(X_val, y_val)], verbose=True) + return model, params + + +def fit_catboost( + params, X_train, X_val, y_train, y_val, is_classification: bool = True, cat_features=None +): + from catboost import CatBoostClassifier, CatBoostRegressor + + params.update( + { + "verbose": 100, + } + ) + if not is_classification: + params.update( + { + "loss_function": "RMSE", + "eval_metric": "RMSE", + } + ) + clf = CatBoostRegressor(**params) + else: + clf = CatBoostClassifier(**params) + + clf.fit( + X_train, y_train, eval_set=[(X_val, y_val)], cat_features=None, early_stopping_rounds=50 + ) + return clf, params + + +def fit_lightgbm( + params, X_train, X_val, y_train, y_val, is_classification: bool = True, cat_features=None +): + from lightgbm import LGBMClassifier, LGBMRegressor + + params.update({"verbose": 100}) + + if not is_classification: + params.update( + { + "objective": "regression", + "metric": "rmse", + } + ) + clf = LGBMRegressor(**params) + else: + clf = LGBMClassifier(**params) + + clf.fit( + X_train, + y_train, + eval_set=[(X_val, y_val)], + categorical_feature=cat_features, + callbacks=[ + lightgbm.early_stopping(stopping_rounds=50, verbose=False), + lightgbm.log_evaluation(period=2000), + ], + ) + return clf, params + + +def fit_rf(params, X_train, X_val, y_train, y_val, is_classification: bool = True): + from sklearn.ensemble import RandomForestClassifier, RandomForestRegressor + + params.update({"verbose": 1}) + if not is_classification: + params.update({"n_jobs": -1, "criterion": "squared_error"}) + clf = RandomForestRegressor(**params) + else: + clf = RandomForestClassifier(**params) + clf.fit(np.concatenate([X_train, X_val], axis=0), np.concatenate([y_train, y_val], axis=0)) + return clf, params + + +def fit_svm(params, X_train, X_val, y_train, y_val): + from sklearn.svm import SVC + + params.update({"kernel": "linear", "probability": True, "verbose": True}) + clf = SVC(**params) + clf.fit(np.concatenate([X_train, X_val], axis=0), np.concatenate([y_train, y_val], axis=0)) + return clf, params + + +def fit_logistics(params, X_train, X_val, y_train, y_val, is_classification=True): + from sklearn.linear_model import LinearRegression, LogisticRegression + + if is_classification: + params.update( + { + "verbose": 1, + "penalty": "l2", + } + ) + clf = LogisticRegression(**params) + else: + params.pop("C", None) + clf = LinearRegression(**params) + clf.fit(np.concatenate([X_train, X_val], axis=0), np.concatenate([y_train, y_val], axis=0)) + return clf, params + + +def fit_adaboost(params, X_train, X_val, y_train, y_val): + from sklearn.ensemble import AdaBoostClassifier + + params.update({"random_state": 0}) + clf = AdaBoostClassifier(**params) + clf.fit(np.concatenate([X_train, X_val], axis=0), np.concatenate([y_train, y_val], axis=0)) + return clf, params + + +def fit_knn(params, X_train, X_val, y_train, y_val, is_classification=True, weight_fn=None): + from scipy.spatial import distance + from sklearn.neighbors import KNeighborsClassifier, KNeighborsRegressor + + if weight_fn is not None: + + def my_weight_fn(x, y): + return distance.minkowski(x, y, p=2, w=weight_fn) + + params.update(dict(metric=my_weight_fn)) + + if is_classification: + # from ehrret import get_retrieval_weight + # def my_distance(weights): + # weights = get_retrieval_weight('feattype', 'eicu', 'READMISSION', feature_names=feature_names) + # return weights + clf = KNeighborsClassifier(**params) + else: + clf = KNeighborsRegressor(**params) + clf.fit(np.concatenate([X_train, X_val], axis=0), np.concatenate([y_train, y_val], axis=0)) + return clf, params + + +def fit_mlp(params, X_train, X_val, y_train, y_val, is_classification=True): + from sklearn.neural_network import MLPClassifier, MLPRegressor + + hidden_layer_sizes = params.pop("type", "2-layer") + if hidden_layer_sizes == "2-layer": + params["hidden_layer_sizes"] = (X_train.shape[1], 64) + elif hidden_layer_sizes == "3-layer": + params["hidden_layer_sizes"] = (X_train.shape[1], 128, 64) + + params.update( + { + "early_stopping": True, + "validation_fraction": 0.2, + } + ) + if is_classification: + clf = MLPClassifier(**params) + else: + clf = MLPRegressor(**params) + + clf.fit(np.concatenate([X_train, X_val], axis=0), np.concatenate([y_train, y_val], axis=0)) + return clf, params + + +def objective( + trial, + X_train, + X_val, + y_train, + y_val, + is_classification=True, + method="xgboost", + cat_features=None, + extra_params: dict | None = None, +): + if extra_params is None: + extra_params = {} + if wandb.run is not None: + wandb.run.config.update( + { + "MODEL": { + "MODEL_NAME": method, + }, + "MODEL_NAME": method, + "FOLD": os.environ.get("FOLD", None), + "TASK_NAME": os.environ.get("TASK_NAME", None), + } + ) + + len(np.unique(y_train)) + + assert method in [ + "xgboost", + "rf", + "svm", + "logistics", + "adaboost", + "catboost", + "lightgbm", + "knn", + "mlp", + ], f"{method} is not supported" + + if method == "xgboost": + model, params = fit_xgboost( + { + "n_estimators": trial.suggest_int( + "n_estimators", 100, 300 + ), # The number of sequential trees to be modeled + "max_depth": trial.suggest_int( + "max_depth", 1, 9 + ), # The maximum depth of a tree.higher depth will allow model to learn relations very specific to a particular sample. Should be tuned + "learning_rate": trial.suggest_float( + "learning_rate", 0.001, 1.0 + ), # impact of each tree on the final outcome + "gamma": trial.suggest_float( + "gamma", 0.001, 1.0 + ), # This will anyways be tuned later. + "reg_alpha": trial.suggest_float( + "reg_alpha", 0.001, 1.0 + ), # This will anyways be tuned later. + "reg_lambda": trial.suggest_float( + "reg_lambda", 0.001, 1.0 + ), # This will anyways be tuned later. + }, + X_train, + X_val, + y_train, + y_val, + is_classification=is_classification, + ) + + elif method == "catboost": + model, params = fit_catboost( + { + "iterations": trial.suggest_int("iterations", 100, 300), + "depth": trial.suggest_int("depth", 1, 9), + "learning_rate": trial.suggest_float("learning_rate", 0.001, 1.0), + "l2_leaf_reg": trial.suggest_float("l2_leaf_reg", 0.001, 1.0), + }, + X_train, + X_val, + y_train, + y_val, + is_classification=is_classification, + cat_features=cat_features, + ) + + elif method == "lightgbm": + model, params = fit_lightgbm( + { + "n_estimators": trial.suggest_int( + "n_estimators", 100, 300 + ), # The number of sequential trees to be modeled + "max_depth": trial.suggest_int( + "max_depth", 1, 9 + ), # The maximum depth of a tree.higher depth will allow model to learn relations very specific to a particular sample. Should be tuned + "learning_rate": trial.suggest_float( + "learning_rate", 0.001, 1.0 + ), # impact of each tree on the final outcome + "reg_alpha": trial.suggest_float( + "reg_alpha", 0.001, 1.0 + ), # This will anyways be tuned later. + "reg_lambda": trial.suggest_float( + "reg_lambda", 0.001, 1.0 + ), # This will anyways be tuned later. + }, + X_train, + X_val, + y_train, + y_val, + is_classification=is_classification, + cat_features=cat_features, + ) + + elif method == "rf": + model, params = fit_rf( + { + "n_estimators": trial.suggest_int( + "n_estimators", 100, 300 + ), # The number of sequential trees to be modeled + "max_depth": trial.suggest_int( + "max_depth", 1, 9 + ), # The maximum depth of a tree.higher depth will allow model to learn relations very specific to a particular sample. Should be tuned + # 'criterion': trial.suggest_categorical("criterion", ["gini", "log_loss", "entropy"]), + "min_samples_split": trial.suggest_int("min_samples_split", 10, 30), + "min_samples_leaf": trial.suggest_int("min_samples_leaf", 3, 10), + }, + X_train, + X_val, + y_train, + y_val, + is_classification=is_classification, + ) + + elif method == "svm": + model, params = fit_svm( + { + "gamma": trial.suggest_float("gamma", 0.001, 1.0), + "C": trial.suggest_float("C", 0.001, 1.0), + }, + X_train, + X_val, + y_train, + y_val, + is_classification=is_classification, + ) + + elif method == "logistics": + model, params = fit_logistics( + { + "C": trial.suggest_float("C", 0.001, 1.0), + }, + X_train, + X_val, + y_train, + y_val, + is_classification=is_classification, + ) + + elif method == "adaboost": + model, params = fit_adaboost( + { + "n_estimators": trial.suggest_int( + "n_estimators", 100, 300 + ), # The number of sequential trees to be modeled + "learning_rate": trial.suggest_float( + "learning_rate", 0.001, 1.0 + ), # impact of each tree on the final outcome + }, + X_train, + X_val, + y_train, + y_val, + is_classification=is_classification, + ) + + elif method == "knn": + model, params = fit_knn( + { + "n_neighbors": trial.suggest_int("n_neighbors", 3, 20), + "weights": trial.suggest_categorical("weights", ["uniform", "distance"]), + "p": trial.suggest_int("p", 1, 2), # 1=manhattan, 2=euclidean + }, + X_train, + X_val, + y_train, + y_val, + is_classification=is_classification, + weight_fn=extra_params.get("weight_fn"), + ) + + elif method == "mlp": + model, params = fit_mlp( + { + "type": trial.suggest_categorical("type", ["2-layer", "3-layer"]), + "activation": trial.suggest_categorical("activation", ["relu", "tanh"]), + "solver": trial.suggest_categorical("solver", ["adam", "sgd"]), + "alpha": trial.suggest_float("alpha", 1e-5, 1e-1, log=True), + "learning_rate": trial.suggest_categorical( + "learning_rate", ["constant", "adaptive"] + ), + }, + X_train, + X_val, + y_train, + y_val, + is_classification=is_classification, + ) + + else: + raise NotImplementedError() + + # Validate the model + if is_classification: + preds = model.predict_proba(X_val) + pred_labels = np.argmax(preds, axis=1) + else: + preds = model.predict(X_val) + pred_labels = preds + + if wandb.run is not None: + wandb.run.config.update(params) + + # Evaluate the model + if not is_classification: + rmse = np.sqrt(np.mean((y_val - pred_labels) ** 2)) + # Log the metrics to wandb + if wandb.run is not None: + wandb.run.log( + { + "Validation/RMSE": rmse, + } + ) + return float(rmse) + else: + # is_binary = len(np.unique(y_val)) == 2 + # if is_binary: + # f1score = f1_score(y_val, pred_labels, average='binary', pos_label=1) + # precision = precision_score(y_val, pred_labels, average='binary', pos_label=1) + # recall = recall_score(y_val, pred_labels, average='binary', pos_label=1) + # else: + f1score = f1_score(y_val, pred_labels, average="macro") + precision = precision_score(y_val, pred_labels, average="macro") + recall = recall_score(y_val, pred_labels, average="macro") + mcc_score = matthews_corrcoef(y_val, pred_labels) + accuracy = accuracy_score(y_val, pred_labels) + + # Log the metrics to wandb + if wandb.run is not None: + wandb.run.log( + { + "Validation/Accuracy": accuracy, + "Validation/F1": f1score, + "Validation/MCC": mcc_score, + "Validation/Precision": precision, + "Validation/Recall": recall, + } + ) + return float(f1score) + + +class TradMLTuner: + def __init__( + self, + storage: str = None, + study_name: str = None, + n_trials: int = 100, + direction: str = "maximize", + pruner=None, + sampler=None, + save_dir: str = None, + method: str = "xgboost", + use_best_params: bool = False, + wandb_kwargs: dict | None = None, + **kwargs, + ) -> None: + self.storage = None + if storage is not None and storage.endswith(".log"): + self.storage = JournalStorage(JournalFileStorage(storage)) + + self.save_dir = save_dir + self.study_name = study_name + self.n_trials = n_trials + self.direction = direction + self.pruner = pruner + self.sampler = sampler + self.save_dir = save_dir + self.method = method + self.use_best_params = use_best_params + self.feature_names = kwargs.get("feature_names") + self.classnames = kwargs.get("classnames") + if save_dir is not None: + os.makedirs(save_dir, exist_ok=True) + + self.study = optuna.create_study( + study_name=study_name, + direction=direction, + storage=self.storage, + load_if_exists=True, + pruner=pruner, + sampler=sampler, + ) + + if wandb_kwargs is not None: + # Initialise wandb callback + self.wandb_kwargs = { + "entity": wandb_kwargs.get("entity", "kaylode"), + "project": wandb_kwargs.get("project", "tabpfn-ehr"), + "reinit": True, + "group": "tabpfn-ehr-optuna", + "job_type": "optuna", + "tags": [self.method, "optuna"] + wandb_kwargs.get("tags", []), + "resume": "allow", + "name": f"{self.method}", + "dir": self.save_dir, + } + self.WANDB_CALLBACK = WeightsAndBiasesCallback( + metric_name="Validation/F1", wandb_kwargs=self.wandb_kwargs, as_multirun=True + ) + else: + self.wandb_kwargs = None + self.WANDB_CALLBACK = None + + def tune( + self, + X_train, + X_val, + y_train, + y_val, + is_classification: bool = True, + cat_features=None, + extra_params: dict | None = None, + ): + if extra_params is None: + extra_params = {} + def wrapped_objective(trial): + return objective( + trial, + X_train, + X_val, + y_train, + y_val, + is_classification=is_classification, + method=self.method, + cat_features=cat_features, + extra_params=extra_params, + ) + + callbacks = None + if self.WANDB_CALLBACK is not None: + decorator = self.WANDB_CALLBACK.track_in_wandb() + wrapped_objective = decorator(wrapped_objective) + callbacks = [] + callbacks.append(self.WANDB_CALLBACK) + + if not self.use_best_params: + try: + self.study.optimize(wrapped_objective, n_trials=self.n_trials, callbacks=callbacks) + except KeyboardInterrupt: + LOGGER.text("KeyboardInterrupt", level=LoggerObserver.ERROR) + best_trial = self.study.best_trial + self.save_best_config(best_trial.params) + + if self.wandb_kwargs is not None: + wandb_kwargs = { + "name": f"{self.method}", + "entity": self.wandb_kwargs.get("entity", "kaylode"), + "project": self.wandb_kwargs.get("project", "tabpfn-ehr"), + "reinit": True, + "group": "tabpfn-ehr-finetune", + "job_type": "train", + "tags": list( + set([self.method, "optuna", "best"] + self.wandb_kwargs.get("tags", [])) + ), + "resume": "allow", + "dir": self.save_dir, + } + wandb.init(**wandb_kwargs) + + if self.save_dir is not None: + leaderboard_df = self.leaderboard() + leaderboard_df.to_csv(osp.join(self.save_dir, "leaderboard.csv")) + leaderboard_df.to_json(osp.join(self.save_dir, "leaderboard.json"), orient="records") + LOGGER.text( + f"Leaderboard saved to {self.save_dir}/leaderboard.csv", level=LoggerObserver.INFO + ) + figs = self.visualize("all") + os.makedirs(osp.join(self.save_dir, "figures"), exist_ok=True) + for fig_name, fig in figs: + try: + fig.write_image(osp.join(self.save_dir, "figures", f"{fig_name}.png")) + LOGGER.text( + f"{fig_name} plot saved to {self.save_dir}/{fig_name}.png", + level=LoggerObserver.INFO, + ) + except Exception: + pass + + # Log the best trial to wandb + if self.wandb_kwargs is not None: + wandb.run.config.update( + { + "MODEL": { + "MODEL_NAME": self.method, + }, + "MODEL_NAME": self.method, + "FOLD": os.environ.get("FOLD", None), + "TASK_NAME": os.environ.get("TASK_NAME", None), + } + ) + + if self.method == "xgboost": + best_model, params = fit_xgboost( + best_trial.params, + X_train, + X_val, + y_train, + y_val, + is_classification=is_classification, + ) + elif self.method == "catboost": + best_model, params = fit_catboost( + best_trial.params, + X_train, + X_val, + y_train, + y_val, + is_classification=is_classification, + ) + elif self.method == "lightgbm": + best_model, params = fit_lightgbm( + best_trial.params, + X_train, + X_val, + y_train, + y_val, + is_classification=is_classification, + ) + elif self.method == "rf": + best_model, params = fit_rf( + best_trial.params, + X_train, + X_val, + y_train, + y_val, + is_classification=is_classification, + ) + elif self.method == "svm": + best_model, params = fit_svm( + best_trial.params, + X_train, + X_val, + y_train, + y_val, + is_classification=is_classification, + ) + elif self.method == "logistics": + best_model, params = fit_logistics( + best_trial.params, + X_train, + X_val, + y_train, + y_val, + is_classification=is_classification, + ) + elif self.method == "adaboost": + best_model, params = fit_adaboost( + best_trial.params, + X_train, + X_val, + y_train, + y_val, + is_classification=is_classification, + ) + elif self.method == "knn": + best_model, params = fit_knn( + best_trial.params, + X_train, + X_val, + y_train, + y_val, + is_classification=is_classification, + weight_fn=extra_params.get("weight_fn"), + ) + elif self.method == "mlp": + best_model, params = fit_mlp( + best_trial.params, + X_train, + X_val, + y_train, + y_val, + is_classification=is_classification, + ) + else: + raise NotImplementedError() + + if self.wandb_kwargs is not None: + wandb.run.config.update(params) + + # Evaluate the model + if not is_classification: + preds = best_model.predict(X_val) + pred_labels = preds + else: + preds = best_model.predict_proba(X_val) + pred_labels = np.argmax(preds, axis=1) + + if is_classification: + # is_binary = len(np.unique(y_val)) == 2 + # if is_binary: + # f1score = f1_score(y_val, pred_labels, average='binary', pos_label=1) + # precision = precision_score(y_val, pred_labels, average='binary', pos_label=1) + # recall = recall_score(y_val, pred_labels, average='binary', pos_label=1) + # else: + f1score = f1_score(y_val, pred_labels, average="macro") + precision = precision_score(y_val, pred_labels, average="macro") + recall = recall_score(y_val, pred_labels, average="macro") + mcc_score = matthews_corrcoef(y_val, pred_labels) + accuracy = accuracy_score(y_val, pred_labels) + # Log the metrics to wandb + if self.wandb_kwargs is not None: + wandb.run.log( + { + "Validation/Accuracy": accuracy, + "Validation/F1": f1score, + "Validation/MCC": mcc_score, + "Validation/Precision": precision, + "Validation/Recall": recall, + } + ) + else: + rmse = np.sqrt(np.mean((y_val - pred_labels) ** 2)) + # Log the metrics to wandb + if self.wandb_kwargs is not None: + wandb.run.log( + { + "Validation/RMSE": rmse, + } + ) + LOGGER.text( + f"Best trial: {best_trial.number} with value: {best_trial.value}", + level=LoggerObserver.INFO, + ) + + if self.method == "logistics": + # Save coefficient and intercept in json + coef = best_model.coef_ + intercept = best_model.intercept_ + with open(osp.join(self.save_dir, "logistics_params.json"), "w") as f: + json.dump( + { + "coef": coef.tolist(), + "intercept": intercept.tolist(), + "feature_names": self.feature_names, + }, + f, + indent=4, + ) + + if is_classification and self.feature_names is not None and self.classnames is not None: + try: + fig = visualize_shap( + best_model, + X_val, + feature_names=self.feature_names, + classnames=self.classnames, + plot_type="dot", + plot_size=(12, 6), + cross_validation=False, + ) + fig.savefig(osp.join(self.save_dir, "shap_summary.png"), bbox_inches="tight") + if self.wandb_kwargs is not None: + wandb.log({"Shap Summary": wandb.Image(fig)}) + LOGGER.text( + f"Shap Summary plot saved to {self.save_dir}/shap_summary.png", + level=LoggerObserver.INFO, + ) + except Exception: + pass + else: + LOGGER.text( + "Shap values are not supported for regression tasks.", level=LoggerObserver.WARN + ) + + if self.wandb_kwargs is not None: + wandb.finish() + + return best_model + + def save_best_config(self, best_params: dict): + with open(os.path.join(self.save_dir, "best_config.json"), "w") as f: + json.dump(best_params, f, indent=4) + + LOGGER.text( + f"Best config saved to {self.save_dir}/best_config.json", level=LoggerObserver.INFO + ) + + def leaderboard(self): + """Print leaderboard of all trials""" + df = self.study.trials_dataframe() + df.columns = [col.replace("user_attrs_", "") for col in df.columns] + return df + + def visualize(self, plot: str, plot_params: dict = None): + """Visualize everything""" + + if plot_params is None: + plot_params = {} + allow_plot_types = [ + "history", + "contour", + "edf", + "intermediate_values", + "parallel_coordinate", + "param_importances", + "slice", + ] + assert plot in ["all", *allow_plot_types], f"{plot} is not supported by Optuna" + + if plot == "all": + fig = [] + for plot_type in allow_plot_types: + one_fig = self.visualize(plot_type, plot_params) + if one_fig is not None: + fig.append((plot_type, one_fig)) + else: + try: + if plot == "history": + fig = plot_optimization_history(self.study, **plot_params) + elif plot == "contour": + fig = plot_contour(self.study, **plot_params) + elif plot == "edf": + fig = plot_edf(self.study, **plot_params) + elif plot == "intermediate_values": + fig = plot_intermediate_values(self.study) + elif plot == "parallel_coordinate": + fig = plot_parallel_coordinate(self.study, **plot_params) + elif plot == "param_importances": + fig = plot_param_importances(self.study, **plot_params) + elif plot == "slice": + fig = plot_slice(self.study, **plot_params) + else: + LOGGER.text(f"{plot} is not supported by Optuna", level=LoggerObserver.ERROR) + raise ValueError() + + if plot != "all" and self.wandb_kwargs is not None: + wandb.log({f"Plot/{plot}": fig}) + + except Exception as e: + LOGGER.text(f"Plotting error: {e}", level=LoggerObserver.ERROR) + return None + + return fig diff --git a/theseus/ml/trainer/__init__.py b/theseus/ml/trainer/__init__.py deleted file mode 100644 index 05ca80ba..00000000 --- a/theseus/ml/trainer/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from theseus.base.trainer import TRAINER_REGISTRY - -from .ml_trainer import MLTrainer - -TRAINER_REGISTRY.register(MLTrainer) diff --git a/theseus/ml/trainer/ml_trainer.py b/theseus/ml/trainer/ml_trainer.py deleted file mode 100644 index 00bbe0a4..00000000 --- a/theseus/ml/trainer/ml_trainer.py +++ /dev/null @@ -1,66 +0,0 @@ -from theseus.base.utilities.loggers.observer import LoggerObserver -from theseus.ml.callbacks import CallbacksList - -LOGGER = LoggerObserver.getLogger("main") - - -class MLTrainer: - def __init__( - self, model, trainset, valset, metrics, callbacks=None, **kwargs - ) -> None: - - if callbacks is not None and not isinstance(callbacks, CallbacksList): - callbacks = callbacks if isinstance(callbacks, list) else [callbacks] - callbacks = CallbacksList(callbacks) - callbacks.set_params({"trainer": self}) - self.callbacks = callbacks - - self.model = model - self.trainset = trainset - self.valset = valset - self.metrics = metrics - - def fit(self): - - # On start callbacks - self.callbacks.run("on_start") - self.callbacks.run("on_train_epoch_start") - self.model.fit( - (self.trainset["inputs"], self.trainset["targets"]), - (self.valset["inputs"], self.valset["targets"]), - ) - self.callbacks.run( - "on_train_epoch_end", - {"trainset": self.trainset, "valset": self.valset}, - ) - - self.callbacks.run("on_validation_epoch_start") - metric_dict = self.validate() - self.callbacks.run( - "on_validation_epoch_end", - { - "iters": 0, - "trainset": self.trainset, - "valset": self.valset, - "metric_dict": metric_dict, - }, - ) - self.callbacks.run("on_finish") - - def validate(self): - """ - Perform validation one epoch - """ - - X_test, y_test = self.valset["inputs"], self.valset["targets"] - y_pred = self.model.predict(X_test, return_probs=True) - score_dict = {} - - if self.metrics is not None: - for metric in self.metrics: - score_dict.update( - metric.value( - {"outputs": y_pred}, {"inputs": X_test, "targets": y_test} - ) - ) - return score_dict diff --git a/theseus/ml/utilities/pprint.py b/theseus/ml/utilities/pprint.py deleted file mode 100644 index 21895290..00000000 --- a/theseus/ml/utilities/pprint.py +++ /dev/null @@ -1,24 +0,0 @@ -import pandas as pd -from tabulate import tabulate - -from theseus.base.utilities.loggers.observer import LoggerObserver - -LOGGER = LoggerObserver.getLogger("main") - - -def pretty_print_df(df, showindex=False): - if isinstance(df, pd.DataFrame): - LOGGER.text( - "\n" - + tabulate(df, headers=df.columns, tablefmt="psql", showindex=showindex), - level=LoggerObserver.INFO, - ) - elif isinstance(df, pd.Series): - df_list = [i for i in zip(df.index.values.tolist(), df.values.tolist())] - LOGGER.text( - "\n" - + tabulate(df_list, headers="keys", tablefmt="psql", showindex=showindex), - level=LoggerObserver.INFO, - ) - else: - raise ValueError() diff --git a/theseus/ml/visualize.py b/theseus/ml/visualize.py new file mode 100644 index 00000000..dea95123 --- /dev/null +++ b/theseus/ml/visualize.py @@ -0,0 +1,157 @@ + +import matplotlib.pyplot as plt +import numpy as np +import plotly.graph_objects as go +import seaborn as sns +from lime import lime_tabular +from sklearn.inspection import permutation_importance + + +def plot_cfm(cm, ax, labels: list): + """ + Make confusion matrix figure + labels: `Optional[List]` + classnames for visualization + """ + ax = sns.heatmap(cm, annot=False, fmt="", cmap="Blues", ax=ax) + ax.set_xlabel("\nActual") + ax.set_ylabel("Predicted ") + ax.xaxis.set_ticklabels(labels, rotation=90) + ax.yaxis.set_ticklabels(labels, rotation=0) + + +def make_cm_fig(cms, labels: list | None = None): + if cms.shape[0] > 1: # multilabel + num_classes = cms.shape[0] + else: + num_classes = cms.shape[1] + + ## Ticket labels - List must be in alphabetical order + if not labels: + labels = [str(i) for i in range(num_classes)] + + ## + num_cfms = cms.shape[0] + nrow = int(np.ceil(np.sqrt(num_cfms))) + + # Clear figures first to prevent memory-consuming + plt.cla() + plt.clf() + plt.close() + + fig, axes = plt.subplots(nrow, nrow, figsize=(8, 8)) + + if num_cfms > 1: + for ax, cfs_matrix, label in zip(axes.flatten(), cms, labels): + ax.set_title(f"{label}\n\n") + plot_cfm(cfs_matrix, ax, labels=["N", "Y"]) + else: + plot_cfm(cms[0], axes, labels=labels) + + fig.tight_layout() + return fig + + +def visualize_shap( + model, + inputs, + feature_names, + classnames, + plot_type="bar", + plot_size="auto", + cross_validation=False, +): + import shap + + if cross_validation: + assert isinstance(model, list), "model must be a list of models" + all_shap_values = [] + + if isinstance(inputs, list): + for _, (m, input) in enumerate(zip(model, inputs)): + explainer = shap.TreeExplainer(m) + shap_values = explainer.shap_values(input, check_additivity=False) + all_shap_values.append(shap_values) + else: + for _, m in enumerate(model): + explainer = shap.TreeExplainer(m) + shap_values = explainer.shap_values(inputs, check_additivity=False) + all_shap_values.append(shap_values) + + if isinstance(feature_names[0], list): # each fold have different order of features + feat_shap_dict = {} + for feature_list, shap_values in zip(feature_names, all_shap_values): + shap_values = np.array(shap_values).T + for feat, shap_val in zip(feature_list, shap_values): + if feat not in feat_shap_dict: + feat_shap_dict[feat] = [] + feat_shap_dict[feat].append(shap_val) + + feat_shap_dict = {k: np.mean(v, axis=0) for k, v in feat_shap_dict.items()} + feat_shap_dict.pop("none", None) + feature_names = sorted(list(feat_shap_dict.keys())) + average_shap_values = np.array([feat_shap_dict[feat] for feat in feature_names]).T + else: + all_shap_values = np.array(all_shap_values) + average_shap_values = np.mean(all_shap_values, axis=0) + # std_shap_values = np.std(all_shap_values, axis=0) + # range_shap_values = np.max(all_shap_values, axis=0) - np.min(all_shap_values, axis=0) + plt.clf() + + shap.summary_plot( + average_shap_values, + inputs, + plot_type=plot_type, + feature_names=feature_names, + class_names=classnames, + show=False, + plot_size=plot_size, + ) + fig = plt.gcf() + else: + explainer = shap.TreeExplainer(model) + shap_values = explainer.shap_values(inputs, check_additivity=False) + plt.clf() + shap.summary_plot( + shap_values, + inputs, + plot_type=plot_type, + feature_names=feature_names, + class_names=classnames, + show=False, + plot_size=plot_size, + ) + fig = plt.gcf() + return fig + + +def visualize_feature_importance(model, inputs, targets, feature_names): + perm_importance = permutation_importance(model, inputs, targets) + sorted_idx = perm_importance.importances_mean.argsort() + + fig = go.Figure( + go.Bar( + x=perm_importance.importances_mean[sorted_idx], + y=[feature_names[i] for i in sorted_idx], + orientation="h", + ) + ) + + fig.update_layout(title=go.layout.Title(text="Permutation Importance", x=0)) + return fig + + +def visualize_lime_instance(training_data, proba_func, item, feature_names=None, class_names=None): + """ + Get explaination for a single instance + """ + explainer = lime_tabular.LimeTabularExplainer( + training_data=training_data, + feature_names=feature_names, + class_names=class_names, + mode="classification", + discretize_continuous=False, + ) + + fig = explainer.explain_instance(data_row=item, predict_fn=proba_func) + return fig diff --git a/theseus/nlp/base/preprocessors/basic_processors.py b/theseus/nlp/base/preprocessors/basic_processors.py index 1e9f4bba..8de387de 100644 --- a/theseus/nlp/base/preprocessors/basic_processors.py +++ b/theseus/nlp/base/preprocessors/basic_processors.py @@ -43,7 +43,7 @@ def __init__(self, language="english"): def __call__(self, x): tokens = word_tokenize(x) - tokens = [word for word in tokens if not word in self.stopwords_list] + tokens = [word for word in tokens if word not in self.stopwords_list] result = " ".join(tokens) return result @@ -135,10 +135,10 @@ class RemoveEmoji(BaseProcessor): def __init__(self) -> None: self.regrex = re.compile( pattern="[" - "\U0001F600-\U0001F64F" # emoticons - "\U0001F300-\U0001F5FF" # symbols & pictographs - "\U0001F680-\U0001F6FF" # transport & map symbols - "\U0001F1E0-\U0001F1FF" # flags (iOS) + "\U0001f600-\U0001f64f" # emoticons + "\U0001f300-\U0001f5ff" # symbols & pictographs + "\U0001f680-\U0001f6ff" # transport & map symbols + "\U0001f1e0-\U0001f1ff" # flags (iOS) "]+", flags=re.UNICODE, ) @@ -178,7 +178,9 @@ class PreprocessCompose(BaseProcessor): :input: list of texts """ - def __init__(self, preprocess_list=[]) -> None: + def __init__(self, preprocess_list=None) -> None: + if preprocess_list is None: + preprocess_list = [] self.preprocess_list = preprocess_list def __call__(self, text): @@ -188,7 +190,6 @@ def __call__(self, text): if __name__ == "__main__": - text = [ "Nick likes to play football, however he is not too fond of tennis. \U0001f602", "Hello there, i'm Kay", diff --git a/theseus/nlp/base/preprocessors/pyvncore_processor.py b/theseus/nlp/base/preprocessors/pyvncore_processor.py index 9bca25ff..fac743fc 100644 --- a/theseus/nlp/base/preprocessors/pyvncore_processor.py +++ b/theseus/nlp/base/preprocessors/pyvncore_processor.py @@ -7,9 +7,7 @@ class VNCoreNLPProcessor(BaseProcessor): def __init__(self, save_dir: str = "/mnt/4TBSSD/zalo/e2e_qa"): py_vncorenlp.download_model(save_dir=save_dir) # Load the word and sentence segmentation component - self.rdrsegmenter = py_vncorenlp.VnCoreNLP( - annotators=["wseg"], save_dir=save_dir - ) + self.rdrsegmenter = py_vncorenlp.VnCoreNLP(annotators=["wseg"], save_dir=save_dir) def __call__(self, text): text = self.rdrsegmenter.word_segment(text) diff --git a/theseus/nlp/base/preprocessors/vocabulary.py b/theseus/nlp/base/preprocessors/vocabulary.py index e7e3a234..c52b5067 100644 --- a/theseus/nlp/base/preprocessors/vocabulary.py +++ b/theseus/nlp/base/preprocessors/vocabulary.py @@ -7,7 +7,7 @@ LOGGER = LoggerObserver.getLogger("main") -class Vocabulary(object): +class Vocabulary: def __init__( self, max_size=None, @@ -72,30 +72,27 @@ def save_vocab(self, save_path): dirname = osp.dirname(save_path) filename, _ = osp.splitext(osp.basename(save_path)) with open(osp.join(dirname, f"{filename}_vocab.txt"), "w") as f: - for term in self.word2idx.keys(): + for term in self.word2idx: f.write(term + "\n") LOGGER.text(f"Save pickle to {save_path}", level=LoggerObserver.INFO) def build_vocab(self, list_tokens, add_special_tokens=True): """Populate the dictionaries for converting tokens to integers (and vice-versa).""" for tok in list_tokens: - if not tok in self.frequency: + if tok not in self.frequency: self.frequency[tok] = 0 self.frequency[tok] += 1 for tok in list(self.frequency.keys()): - if self.max_freq is not None: - if self.frequency[tok] > self.max_freq: - self.frequency.pop(tok) - continue - if self.min_freq is not None: - if self.frequency[tok] < self.min_freq: - self.frequency.pop(tok) - continue + if self.max_freq is not None and self.frequency[tok] > self.max_freq: + self.frequency.pop(tok) + continue + if self.min_freq is not None and self.frequency[tok] < self.min_freq: + self.frequency.pop(tok) + continue list_tokens = [ - k - for k, _ in sorted(self.frequency.items(), key=lambda x: x[1], reverse=True) + k for k, _ in sorted(self.frequency.items(), key=lambda x: x[1], reverse=True) ] if self.max_size is not None: list_tokens = list_tokens[: self.max_size] @@ -125,11 +122,11 @@ def add_word(self, word, index=None): if index is None: index = self.vocab_size - if not word in self.word2idx.keys() and not index in self.idx2word.keys(): + if word not in self.word2idx and index not in self.idx2word: self.word2idx[word] = self.vocab_size self.idx2word[self.vocab_size] = word self.vocab_size += 1 - elif not word in self.word2idx.keys() and index in self.idx2word.keys(): + elif word not in self.word2idx and index in self.idx2word: if self.replace: old_word = self.idx2word[index] self.word2idx[old_word] = self.vocab_size @@ -145,7 +142,7 @@ def add_word(self, word, index=None): ) raise ValueError() - elif word in self.word2idx.keys() and not index in self.idx2word.keys(): + elif word in self.word2idx and index not in self.idx2word: if self.replace: old_idx = self.word2idx[word] self.idx2word[old_idx] = None @@ -165,19 +162,19 @@ def add_word(self, word, index=None): raise ValueError() def add_special_tokens(self): - if self.sos_word not in self.special_tokens.keys(): + if self.sos_word not in self.special_tokens: self.add_word(self.sos_word) self.special_tokens.update({self.sos_word: self.vocab_size}) - if self.eos_word not in self.special_tokens.keys(): + if self.eos_word not in self.special_tokens: self.add_word(self.eos_word) self.special_tokens.update({self.eos_word: self.vocab_size}) - if self.pad_word not in self.special_tokens.keys(): + if self.pad_word not in self.special_tokens: self.add_word(self.pad_word) self.special_tokens.update({self.pad_word: self.vocab_size}) - if self.unk_word not in self.special_tokens.keys(): + if self.unk_word not in self.special_tokens: self.add_word(self.unk_word) self.special_tokens.update({self.unk_word: self.vocab_size}) @@ -199,7 +196,7 @@ def encode_tokens(self, lists_of_tokens, **kwargs): """ add_special_tokens = kwargs.get("add_special_tokens", False) - max_length = kwargs.get("max_length", None) + max_length = kwargs.get("max_length") return_token_type_ids = kwargs.get("return_token_type_ids", False) truncation = kwargs.get("truncation", False) @@ -238,15 +235,11 @@ def encode_tokens(self, lists_of_tokens, **kwargs): batch = batch[-max_length:] else: LOGGER.text( - f"Sequence is longer than max_length. Please use truncation=True", + "Sequence is longer than max_length. Please use truncation=True", level=LoggerObserver.ERROR, ) raise ValueError() - if ( - len(batch) < max_length - and add_special_tokens - and self.use_special_tokens - ): + if len(batch) < max_length and add_special_tokens and self.use_special_tokens: batch += [self.__call__(self.pad_word)] * (max_length - len(batch)) if return_token_type_ids: @@ -295,12 +288,12 @@ def encode_texts(self, text, **kwargs): return self.encode_tokens(tokenized_texts, **kwargs) def itos(self, idx): - if not idx in self.idx2word: + if idx not in self.idx2word: return self.idx2word[self.__call__(self.unk_word)] return self.idx2word[idx] def __call__(self, word): - if not word in self.word2idx: + if word not in self.word2idx: return self.word2idx[self.unk_word] return self.word2idx[word] diff --git a/theseus/nlp/base/utilities/correct_tone.py b/theseus/nlp/base/utilities/correct_tone.py index f0c4527a..085f5d21 100644 --- a/theseus/nlp/base/utilities/correct_tone.py +++ b/theseus/nlp/base/utilities/correct_tone.py @@ -11,19 +11,17 @@ def normalize_diacritics(source, new_style=False, decomposed=False): diacritics = f"{combining_breve}{combining_circumflex_accent}{combining_horn}" result = unicodedata.normalize("NFD", source) # Put the tone on the second vowel - result = re.sub(r"(?i){}([aeiouy{}]+)".format(tone, diacritics), r"\2\1", result) + result = re.sub(rf"(?i){tone}([aeiouy{diacritics}]+)", r"\2\1", result) # Put the tone on the vowel with a diacritic - result = re.sub(r"(?i)(?<=[{}])(.){}".format(diacritics, tone), r"\2\1", result) + result = re.sub(rf"(?i)(?<=[{diacritics}])(.){tone}", r"\2\1", result) # For vowels that are not oa, oe, uy put the tone on the penultimate vowel - result = re.sub(r"(?i)(?<=[ae])([iouy]){}".format(tone), r"\2\1", result) - result = re.sub(r"(?i)(?<=[oy])([iuy]){}".format(tone), r"\2\1", result) - result = re.sub(r"(?i)(? object mapping, to support third-party users' custom modules. + To create a registry (e.g. a backbone registry): + .. code-block:: python BACKBONE_REGISTRY = Registry('BACKBONE') + To register an object: + .. code-block:: python @BACKBONE_REGISTRY.register() class MyBackbone(): ... + Or: + .. code-block:: python BACKBONE_REGISTRY.register(MyBackbone) + + To merge registries: + + .. code-block:: python + MERGED = BACKBONE_REGISTRY.merge(OTHER_REGISTRY) """ + __slots__ = ("_name", "_obj_map") + def __init__(self, name: str) -> None: - """ - Args: - name (str): the name of this registry - """ self._name: str = name - self._obj_map: Dict[str, Any] = {} - - def _do_register(self, name: str, obj: Any, override: bool = False) -> None: - if not override: - assert ( - name not in self._obj_map - ), "An object named '{}' was already registered in '{}' registry!".format( - name, self._name + self._obj_map: dict[str, T] = {} + + def _do_register(self, name: str, obj: T, override: bool = False) -> None: + if name in self._obj_map and self._obj_map[name] is not obj and not override: + logger.warning( + "An object named '%s' was already registered in '%s' registry!", + name, + self._name, ) + return self._obj_map[name] = obj - def register( - self, obj: Any = None, prefix: str = "", override: bool = False - ) -> Any: + @overload + def register(self, obj: None = None, prefix: str = "", override: bool = False) -> Any: ... + + @overload + def register(self, obj: T, prefix: str = "", override: bool = False) -> None: ... + + def register(self, obj: T | None = None, prefix: str = "", override: bool = False) -> Any: """ - Register the given object under the the name `obj.__name__`. - Can be used as either a decorator or not. See docstring of this class for usage. + Register the given object under the name ``obj.__name__``. + Can be used as either a decorator or not. """ if obj is None: - # used as a decorator + # Used as a decorator def deco(func_or_class: Any) -> Any: name = func_or_class.__name__ self._do_register(prefix + name, func_or_class, override) @@ -55,30 +77,56 @@ def deco(func_or_class: Any) -> Any: return deco - # used as a function call - name = obj.__name__ - self._do_register(prefix + name, obj) + # Used as a function call + name = obj.__name__ # type: ignore[union-attr] + self._do_register(prefix + name, obj, override) - def get(self, name: str) -> Any: + def get(self, name: str) -> T: + """Get registered object by name. Raises KeyError if not found.""" ret = self._obj_map.get(name) if ret is None: raise KeyError( - "No object named '{}' found in '{}' registry!".format(name, self._name) + f"No object named '{name}' found in '{self._name}' registry! " + f"Available: {sorted(self._obj_map.keys())}" ) return ret + def get_or_none(self, name: str) -> T | None: + """Get registered object by name, returns None if not found.""" + return self._obj_map.get(name) + + def keys(self) -> list[str]: + """Return all registered names.""" + return list(self._obj_map.keys()) + + def values(self) -> list[T]: + """Return all registered objects.""" + return list(self._obj_map.values()) + + def merge(self, other: Registry[T], override: bool = False) -> Registry[T]: + """ + Merge another registry into this one. Returns self for chaining. + Useful for task-specific pipelines that extend base registries. + """ + for name, obj in other: + self._do_register(name, obj, override=override) + return self + def __contains__(self, name: str) -> bool: return name in self._obj_map + def __len__(self) -> int: + return len(self._obj_map) + + def __getitem__(self, name: str) -> T: + return self.get(name) + def __repr__(self) -> str: table_headers = ["Names", "Objects"] - table = tabulate( - self._obj_map.items(), headers=table_headers, tablefmt="fancy_grid" - ) - return "Registry of {}:\n".format(self._name) + table + table = tabulate(self._obj_map.items(), headers=table_headers, tablefmt="fancy_grid") + return f"Registry of {self._name}:\n{table}" - def __iter__(self) -> Iterator[Tuple[str, Any]]: + def __iter__(self) -> Iterator[tuple[str, T]]: return iter(self._obj_map.items()) - # pyre-fixme[4]: Attribute must be annotated. __str__ = __repr__ diff --git a/uv.lock b/uv.lock new file mode 100644 index 00000000..b8ceee9f --- /dev/null +++ b/uv.lock @@ -0,0 +1,3926 @@ +version = 1 +revision = 3 +requires-python = ">=3.10, <3.13" +resolution-markers = [ + "python_full_version >= '3.12' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "python_full_version >= '3.12' and platform_machine != 'aarch64' and sys_platform == 'linux'", + "python_full_version >= '3.12' and sys_platform == 'darwin'", + "python_full_version >= '3.12' and sys_platform != 'darwin' and sys_platform != 'linux'", + "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "python_full_version == '3.11.*' and platform_machine != 'aarch64' and sys_platform == 'linux'", + "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'linux'", + "python_full_version == '3.11.*' and sys_platform == 'darwin'", + "python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux'", + "python_full_version < '3.11' and sys_platform == 'darwin'", + "python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux'", +] + +[[package]] +name = "aiohappyeyeballs" +version = "2.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload-time = "2025-03-12T01:42:48.764Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z" }, +] + +[[package]] +name = "aiohttp" +version = "3.13.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohappyeyeballs" }, + { name = "aiosignal" }, + { name = "async-timeout", marker = "python_full_version < '3.11'" }, + { name = "attrs" }, + { name = "frozenlist" }, + { name = "multidict" }, + { 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, upload-time = "2025-10-28T20:59:39.937Z" } +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, upload-time = "2025-10-28T20:55:27.924Z" }, + { 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, upload-time = "2025-10-28T20:55:29.456Z" }, + { 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, upload-time = "2025-10-28T20:55:31.134Z" }, + { 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, upload-time = "2025-10-28T20:55:36.065Z" }, + { 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, upload-time = "2025-10-28T20:55:37.576Z" }, + { 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, upload-time = "2025-10-28T20:55:39.457Z" }, + { 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, upload-time = "2025-10-28T20:55:40.958Z" }, + { 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, upload-time = "2025-10-28T20:55:42.745Z" }, + { 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, upload-time = "2025-10-28T20:55:44.264Z" }, + { 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, upload-time = "2025-10-28T20:55:46.626Z" }, + { 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, upload-time = "2025-10-28T20:55:48.933Z" }, + { 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, upload-time = "2025-10-28T20:55:50.377Z" }, + { 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, upload-time = "2025-10-28T20:55:51.861Z" }, + { 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, upload-time = "2025-10-28T20:55:53.515Z" }, + { 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, upload-time = "2025-10-28T20:55:55.438Z" }, + { url = "https://files.pythonhosted.org/packages/0d/42/d511aff5c3a2b06c09d7d214f508a4ad8ac7799817f7c3d23e7336b5e896/aiohttp-3.13.2-cp310-cp310-win32.whl", hash = "sha256:e574a7d61cf10351d734bcddabbe15ede0eaa8a02070d85446875dc11189a251", size = 432290, upload-time = "2025-10-28T20:55:56.96Z" }, + { url = "https://files.pythonhosted.org/packages/8b/ea/1c2eb7098b5bad4532994f2b7a8228d27674035c9b3234fe02c37469ef14/aiohttp-3.13.2-cp310-cp310-win_amd64.whl", hash = "sha256:364f55663085d658b8462a1c3f17b2b84a5c2e1ba858e1b79bff7b2e24ad1514", size = 455075, upload-time = "2025-10-28T20:55:58.373Z" }, + { url = "https://files.pythonhosted.org/packages/35/74/b321e7d7ca762638cdf8cdeceb39755d9c745aff7a64c8789be96ddf6e96/aiohttp-3.13.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4647d02df098f6434bafd7f32ad14942f05a9caa06c7016fdcc816f343997dd0", size = 743409, upload-time = "2025-10-28T20:56:00.354Z" }, + { url = "https://files.pythonhosted.org/packages/99/3d/91524b905ec473beaf35158d17f82ef5a38033e5809fe8742e3657cdbb97/aiohttp-3.13.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e3403f24bcb9c3b29113611c3c16a2a447c3953ecf86b79775e7be06f7ae7ccb", size = 497006, upload-time = "2025-10-28T20:56:01.85Z" }, + { url = "https://files.pythonhosted.org/packages/eb/d3/7f68bc02a67716fe80f063e19adbd80a642e30682ce74071269e17d2dba1/aiohttp-3.13.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:43dff14e35aba17e3d6d5ba628858fb8cb51e30f44724a2d2f0c75be492c55e9", size = 493195, upload-time = "2025-10-28T20:56:03.314Z" }, + { url = "https://files.pythonhosted.org/packages/98/31/913f774a4708775433b7375c4f867d58ba58ead833af96c8af3621a0d243/aiohttp-3.13.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e2a9ea08e8c58bb17655630198833109227dea914cd20be660f52215f6de5613", size = 1747759, upload-time = "2025-10-28T20:56:04.904Z" }, + { url = "https://files.pythonhosted.org/packages/e8/63/04efe156f4326f31c7c4a97144f82132c3bb21859b7bb84748d452ccc17c/aiohttp-3.13.2-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:53b07472f235eb80e826ad038c9d106c2f653584753f3ddab907c83f49eedead", size = 1704456, upload-time = "2025-10-28T20:56:06.986Z" }, + { url = "https://files.pythonhosted.org/packages/8e/02/4e16154d8e0a9cf4ae76f692941fd52543bbb148f02f098ca73cab9b1c1b/aiohttp-3.13.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e736c93e9c274fce6419af4aac199984d866e55f8a4cec9114671d0ea9688780", size = 1807572, upload-time = "2025-10-28T20:56:08.558Z" }, + { url = "https://files.pythonhosted.org/packages/34/58/b0583defb38689e7f06798f0285b1ffb3a6fb371f38363ce5fd772112724/aiohttp-3.13.2-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ff5e771f5dcbc81c64898c597a434f7682f2259e0cd666932a913d53d1341d1a", size = 1895954, upload-time = "2025-10-28T20:56:10.545Z" }, + { url = "https://files.pythonhosted.org/packages/6b/f3/083907ee3437425b4e376aa58b2c915eb1a33703ec0dc30040f7ae3368c6/aiohttp-3.13.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a3b6fb0c207cc661fa0bf8c66d8d9b657331ccc814f4719468af61034b478592", size = 1747092, upload-time = "2025-10-28T20:56:12.118Z" }, + { url = "https://files.pythonhosted.org/packages/ac/61/98a47319b4e425cc134e05e5f3fc512bf9a04bf65aafd9fdcda5d57ec693/aiohttp-3.13.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:97a0895a8e840ab3520e2288db7cace3a1981300d48babeb50e7425609e2e0ab", size = 1606815, upload-time = "2025-10-28T20:56:14.191Z" }, + { url = "https://files.pythonhosted.org/packages/97/4b/e78b854d82f66bb974189135d31fce265dee0f5344f64dd0d345158a5973/aiohttp-3.13.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9e8f8afb552297aca127c90cb840e9a1d4bfd6a10d7d8f2d9176e1acc69bad30", size = 1723789, upload-time = "2025-10-28T20:56:16.101Z" }, + { url = "https://files.pythonhosted.org/packages/ed/fc/9d2ccc794fc9b9acd1379d625c3a8c64a45508b5091c546dea273a41929e/aiohttp-3.13.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:ed2f9c7216e53c3df02264f25d824b079cc5914f9e2deba94155190ef648ee40", size = 1718104, upload-time = "2025-10-28T20:56:17.655Z" }, + { url = "https://files.pythonhosted.org/packages/66/65/34564b8765ea5c7d79d23c9113135d1dd3609173da13084830f1507d56cf/aiohttp-3.13.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:99c5280a329d5fa18ef30fd10c793a190d996567667908bef8a7f81f8202b948", size = 1785584, upload-time = "2025-10-28T20:56:19.238Z" }, + { url = "https://files.pythonhosted.org/packages/30/be/f6a7a426e02fc82781afd62016417b3948e2207426d90a0e478790d1c8a4/aiohttp-3.13.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:2ca6ffef405fc9c09a746cb5d019c1672cd7f402542e379afc66b370833170cf", size = 1595126, upload-time = "2025-10-28T20:56:20.836Z" }, + { url = "https://files.pythonhosted.org/packages/e5/c7/8e22d5d28f94f67d2af496f14a83b3c155d915d1fe53d94b66d425ec5b42/aiohttp-3.13.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:47f438b1a28e926c37632bff3c44df7d27c9b57aaf4e34b1def3c07111fdb782", size = 1800665, upload-time = "2025-10-28T20:56:22.922Z" }, + { url = "https://files.pythonhosted.org/packages/d1/11/91133c8b68b1da9fc16555706aa7276fdf781ae2bb0876c838dd86b8116e/aiohttp-3.13.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9acda8604a57bb60544e4646a4615c1866ee6c04a8edef9b8ee6fd1d8fa2ddc8", size = 1739532, upload-time = "2025-10-28T20:56:25.924Z" }, + { url = "https://files.pythonhosted.org/packages/17/6b/3747644d26a998774b21a616016620293ddefa4d63af6286f389aedac844/aiohttp-3.13.2-cp311-cp311-win32.whl", hash = "sha256:868e195e39b24aaa930b063c08bb0c17924899c16c672a28a65afded9c46c6ec", size = 431876, upload-time = "2025-10-28T20:56:27.524Z" }, + { url = "https://files.pythonhosted.org/packages/c3/63/688462108c1a00eb9f05765331c107f95ae86f6b197b865d29e930b7e462/aiohttp-3.13.2-cp311-cp311-win_amd64.whl", hash = "sha256:7fd19df530c292542636c2a9a85854fab93474396a52f1695e799186bbd7f24c", size = 456205, upload-time = "2025-10-28T20:56:29.062Z" }, + { url = "https://files.pythonhosted.org/packages/29/9b/01f00e9856d0a73260e86dd8ed0c2234a466c5c1712ce1c281548df39777/aiohttp-3.13.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b1e56bab2e12b2b9ed300218c351ee2a3d8c8fdab5b1ec6193e11a817767e47b", size = 737623, upload-time = "2025-10-28T20:56:30.797Z" }, + { url = "https://files.pythonhosted.org/packages/5a/1b/4be39c445e2b2bd0aab4ba736deb649fabf14f6757f405f0c9685019b9e9/aiohttp-3.13.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:364e25edaabd3d37b1db1f0cbcee8c73c9a3727bfa262b83e5e4cf3489a2a9dc", size = 492664, upload-time = "2025-10-28T20:56:32.708Z" }, + { url = "https://files.pythonhosted.org/packages/28/66/d35dcfea8050e131cdd731dff36434390479b4045a8d0b9d7111b0a968f1/aiohttp-3.13.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c5c94825f744694c4b8db20b71dba9a257cd2ba8e010a803042123f3a25d50d7", size = 491808, upload-time = "2025-10-28T20:56:34.57Z" }, + { url = "https://files.pythonhosted.org/packages/00/29/8e4609b93e10a853b65f8291e64985de66d4f5848c5637cddc70e98f01f8/aiohttp-3.13.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba2715d842ffa787be87cbfce150d5e88c87a98e0b62e0f5aa489169a393dbbb", size = 1738863, upload-time = "2025-10-28T20:56:36.377Z" }, + { url = "https://files.pythonhosted.org/packages/9d/fa/4ebdf4adcc0def75ced1a0d2d227577cd7b1b85beb7edad85fcc87693c75/aiohttp-3.13.2-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:585542825c4bc662221fb257889e011a5aa00f1ae4d75d1d246a5225289183e3", size = 1700586, upload-time = "2025-10-28T20:56:38.034Z" }, + { url = "https://files.pythonhosted.org/packages/da/04/73f5f02ff348a3558763ff6abe99c223381b0bace05cd4530a0258e52597/aiohttp-3.13.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:39d02cb6025fe1aabca329c5632f48c9532a3dabccd859e7e2f110668972331f", size = 1768625, upload-time = "2025-10-28T20:56:39.75Z" }, + { url = "https://files.pythonhosted.org/packages/f8/49/a825b79ffec124317265ca7d2344a86bcffeb960743487cb11988ffb3494/aiohttp-3.13.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e67446b19e014d37342f7195f592a2a948141d15a312fe0e700c2fd2f03124f6", size = 1867281, upload-time = "2025-10-28T20:56:41.471Z" }, + { url = "https://files.pythonhosted.org/packages/b9/48/adf56e05f81eac31edcfae45c90928f4ad50ef2e3ea72cb8376162a368f8/aiohttp-3.13.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4356474ad6333e41ccefd39eae869ba15a6c5299c9c01dfdcfdd5c107be4363e", size = 1752431, upload-time = "2025-10-28T20:56:43.162Z" }, + { url = "https://files.pythonhosted.org/packages/30/ab/593855356eead019a74e862f21523db09c27f12fd24af72dbc3555b9bfd9/aiohttp-3.13.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:eeacf451c99b4525f700f078becff32c32ec327b10dcf31306a8a52d78166de7", size = 1562846, upload-time = "2025-10-28T20:56:44.85Z" }, + { url = "https://files.pythonhosted.org/packages/39/0f/9f3d32271aa8dc35036e9668e31870a9d3b9542dd6b3e2c8a30931cb27ae/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d8a9b889aeabd7a4e9af0b7f4ab5ad94d42e7ff679aaec6d0db21e3b639ad58d", size = 1699606, upload-time = "2025-10-28T20:56:46.519Z" }, + { url = "https://files.pythonhosted.org/packages/2c/3c/52d2658c5699b6ef7692a3f7128b2d2d4d9775f2a68093f74bca06cf01e1/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:fa89cb11bc71a63b69568d5b8a25c3ca25b6d54c15f907ca1c130d72f320b76b", size = 1720663, upload-time = "2025-10-28T20:56:48.528Z" }, + { url = "https://files.pythonhosted.org/packages/9b/d4/8f8f3ff1fb7fb9e3f04fcad4e89d8a1cd8fc7d05de67e3de5b15b33008ff/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8aa7c807df234f693fed0ecd507192fc97692e61fee5702cdc11155d2e5cadc8", size = 1737939, upload-time = "2025-10-28T20:56:50.77Z" }, + { url = "https://files.pythonhosted.org/packages/03/d3/ddd348f8a27a634daae39a1b8e291ff19c77867af438af844bf8b7e3231b/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:9eb3e33fdbe43f88c3c75fa608c25e7c47bbd80f48d012763cb67c47f39a7e16", size = 1555132, upload-time = "2025-10-28T20:56:52.568Z" }, + { url = "https://files.pythonhosted.org/packages/39/b8/46790692dc46218406f94374903ba47552f2f9f90dad554eed61bfb7b64c/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9434bc0d80076138ea986833156c5a48c9c7a8abb0c96039ddbb4afc93184169", size = 1764802, upload-time = "2025-10-28T20:56:54.292Z" }, + { url = "https://files.pythonhosted.org/packages/ba/e4/19ce547b58ab2a385e5f0b8aa3db38674785085abcf79b6e0edd1632b12f/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ff15c147b2ad66da1f2cbb0622313f2242d8e6e8f9b79b5206c84523a4473248", size = 1719512, upload-time = "2025-10-28T20:56:56.428Z" }, + { url = "https://files.pythonhosted.org/packages/70/30/6355a737fed29dcb6dfdd48682d5790cb5eab050f7b4e01f49b121d3acad/aiohttp-3.13.2-cp312-cp312-win32.whl", hash = "sha256:27e569eb9d9e95dbd55c0fc3ec3a9335defbf1d8bc1d20171a49f3c4c607b93e", size = 426690, upload-time = "2025-10-28T20:56:58.736Z" }, + { url = "https://files.pythonhosted.org/packages/0a/0d/b10ac09069973d112de6ef980c1f6bb31cb7dcd0bc363acbdad58f927873/aiohttp-3.13.2-cp312-cp312-win_amd64.whl", hash = "sha256:8709a0f05d59a71f33fd05c17fc11fcb8c30140506e13c2f5e8ee1b8964e1b45", size = 453465, upload-time = "2025-10-28T20:57:00.795Z" }, +] + +[[package]] +name = "aiosignal" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +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" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, +] + +[[package]] +name = "albucore" +version = "0.0.23" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "opencv-python-headless" }, + { name = "simsimd" }, + { name = "stringzilla" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/64/78d1716dd1496734d58705d68e02a9eadf4c10edd32c3ad641dde949efca/albucore-0.0.23.tar.gz", hash = "sha256:57823982b954913b84a9e2cf71058c4577b02397a62c41885be2d9b295efa8ab", size = 16437, upload-time = "2024-12-24T20:28:47.136Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3d/de/4d9298befa6ae0f21230378f55100dca364816e3734028ca2766f2eca263/albucore-0.0.23-py3-none-any.whl", hash = "sha256:99274ac0c15a1a7d9a726df9d54d5ab70d9d0c189e2a935399dba3d4bafad415", size = 14717, upload-time = "2024-12-24T20:28:46.03Z" }, +] + +[[package]] +name = "albumentations" +version = "1.4.24" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "albucore" }, + { name = "numpy" }, + { name = "opencv-python-headless" }, + { name = "pydantic" }, + { name = "pyyaml" }, + { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scipy", version = "1.16.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/04/5a/151344c3ce5a6ee50437b07aa58364ada145d3a7b63e12849068ed0c5265/albumentations-1.4.24.tar.gz", hash = "sha256:cc8874cb0abccc9117aefacbe385c354ac824fa142f46a6c0896d65cec5f8a6d", size = 263557, upload-time = "2024-12-24T21:59:34.587Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/af/ae/904b7bf58281d2bc1f3d6d48813bbcee742d1f8e3f9c0e1c451b0f67eb5a/albumentations-1.4.24-py3-none-any.whl", hash = "sha256:2f639257a11e681071f4f7d10f6a6d874ae705bcce52746874cd8d7e317a16d7", size = 274921, upload-time = "2024-12-24T21:59:32.202Z" }, +] + +[[package]] +name = "alembic" +version = "1.17.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mako" }, + { name = "sqlalchemy" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { 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" } +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 = "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" } +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 = "antlr4-python3-runtime" +version = "4.9.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3e/38/7859ff46355f76f8d19459005ca000b6e7012f2f1ca597746cbcd1fbfe5e/antlr4-python3-runtime-4.9.3.tar.gz", hash = "sha256:f224469b4168294902bb1efa80a8bf7855f24c99aef99cbefc1bcd3cce77881b", size = 117034, upload-time = "2021-11-06T17:52:23.524Z" } + +[[package]] +name = "anyio" +version = "4.11.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "idna" }, + { name = "sniffio" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c6/78/7d432127c41b50bccba979505f272c16cbcadcc33645d5fa3a738110ae75/anyio-4.11.0.tar.gz", hash = "sha256:82a8d0b81e318cc5ce71a5f1f8b5c4e63619620b63141ef8c995fa0db95a57c4", size = 219094, upload-time = "2025-09-23T09:19:12.58Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/15/b3/9b1a8074496371342ec1e796a96f99c82c945a339cd81a8e73de28b4cf9e/anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc", size = 109097, upload-time = "2025-09-23T09:19:10.601Z" }, +] + +[[package]] +name = "async-timeout" +version = "5.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a5/ae/136395dfbfe00dfc94da3f3e136d0b13f394cba8f4841120e34226265780/async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3", size = 9274, upload-time = "2024-11-06T16:41:39.6Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", size = 6233, upload-time = "2024-11-06T16:41:37.9Z" }, +] + +[[package]] +name = "attrs" +version = "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" } +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" }, +] + +[[package]] +name = "beautifulsoup4" +version = "4.14.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "soupsieve" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/77/e9/df2358efd7659577435e2177bfa69cba6c33216681af51a707193dec162a/beautifulsoup4-4.14.2.tar.gz", hash = "sha256:2a98ab9f944a11acee9cc848508ec28d9228abfd522ef0fad6a02a72e0ded69e", size = 625822, upload-time = "2025-09-29T10:05:42.613Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/fe/3aed5d0be4d404d12d36ab97e2f1791424d9ca39c2f754a6285d59a3b01d/beautifulsoup4-4.14.2-py3-none-any.whl", hash = "sha256:5ef6fa3a8cbece8488d66985560f97ed091e22bbc4e9c2338508a9d5de6d4515", size = 106392, upload-time = "2025-09-29T10:05:43.771Z" }, +] + +[[package]] +name = "blis" +version = "1.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d0/d0/d8cc8c9a4488a787e7fa430f6055e5bd1ddb22c340a751d9e901b82e2efe/blis-1.3.3.tar.gz", hash = "sha256:034d4560ff3cc43e8aa37e188451b0440e3261d989bb8a42ceee865607715ecd", size = 2644873, upload-time = "2025-11-17T12:28:30.511Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/db/d80daf6c060618c72acecf026410b806f620cdea62b2e72f3235d7389d05/blis-1.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:650f1d2b28e3c875927c63deebda463a6f9d237dff30e445bfe2127718c1a344", size = 6925724, upload-time = "2025-11-17T12:27:14.23Z" }, + { url = "https://files.pythonhosted.org/packages/06/cd/7ac854c92e33cfccc0eded48e979a9fc26a447952d07a9c7c7da7c1d6eec/blis-1.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9b0d42420ddd543eec51ccb99d38364a0c0833b6895eced37127822de6ecacff", size = 1233606, upload-time = "2025-11-17T12:27:16.107Z" }, + { url = "https://files.pythonhosted.org/packages/c7/ae/ad3165fdbc4ef6afef585686a778c72cd67fb5aa16ab2fd2f4494186705e/blis-1.3.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f0628a030d44aa71cac5973e40c9e95ec767abaaf2fd366a094b9398885f82f2", size = 2769094, upload-time = "2025-11-17T12:27:17.883Z" }, + { url = "https://files.pythonhosted.org/packages/25/d4/7b0820f139b4ea67606d01b59ba6afbee4552ce7b2fd179f2fb7908e294f/blis-1.3.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d0114cf2d8f19e0ed210f9ae92594cd0a12efa1bbbce444028b0fc365bbbb8af", size = 11300520, upload-time = "2025-11-17T12:27:20.058Z" }, + { url = "https://files.pythonhosted.org/packages/85/f3/865a4322bdbeb944744c1908e67fdabecd476613a17204956cff12d568c9/blis-1.3.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7e88181e9dd8430029ebaf22d41bf79e756e8c95363e9471717102c66beb4a6d", size = 2962083, upload-time = "2025-11-17T12:27:22.098Z" }, + { url = "https://files.pythonhosted.org/packages/65/a2/c2842fa1e2e6bd56eb93e41b34859a9af8b5b63669ee0442bea585d8f607/blis-1.3.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:62fb8c731347b0f98f5f81d19d339049e61489798738467d156c66cc329b0754", size = 14177001, upload-time = "2025-11-17T12:27:24.345Z" }, + { url = "https://files.pythonhosted.org/packages/b5/9b/3b1532f23db8bdddf3a976e9acf51e8debd94c63be5dafb8ccbab3e62935/blis-1.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:631836d4f335e62c30aa50a1aa0170773265c73654d296361f95180006e88c04", size = 6184429, upload-time = "2025-11-17T12:27:27.054Z" }, + { url = "https://files.pythonhosted.org/packages/a1/0a/a4c8736bc497d386b0ffc76d321f478c03f1a4725e52092f93b38beb3786/blis-1.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e10c8d3e892b1dbdff365b9d00e08291876fc336915bf1a5e9f188ed087e1a91", size = 6925522, upload-time = "2025-11-17T12:27:29.199Z" }, + { url = "https://files.pythonhosted.org/packages/83/5a/3437009282f23684ecd3963a8b034f9307cdd2bf4484972e5a6b096bf9ac/blis-1.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:66e6249564f1db22e8af1e0513ff64134041fa7e03c8dd73df74db3f4d8415a7", size = 1232787, upload-time = "2025-11-17T12:27:30.996Z" }, + { url = "https://files.pythonhosted.org/packages/d1/0e/82221910d16259ce3017c1442c468a3f206a4143a96fbba9f5b5b81d62e8/blis-1.3.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7260da065958b4e5475f62f44895ef9d673b0f47dcf61b672b22b7dae1a18505", size = 2844596, upload-time = "2025-11-17T12:27:32.601Z" }, + { url = "https://files.pythonhosted.org/packages/6c/93/ab547f1a5c23e20bca16fbcf04021c32aac3f969be737ea4980509a7ca90/blis-1.3.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e9327a6ca67de8ae76fe071e8584cc7f3b2e8bfadece4961d40f2826e1cda2df", size = 11377746, upload-time = "2025-11-17T12:27:35.342Z" }, + { url = "https://files.pythonhosted.org/packages/6e/a6/7733820aa62da32526287a63cd85c103b2b323b186c8ee43b7772ff7017c/blis-1.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c4ae70629cf302035d268858a10ca4eb6242a01b2dc8d64422f8e6dcb8a8ee74", size = 3041954, upload-time = "2025-11-17T12:27:37.479Z" }, + { url = "https://files.pythonhosted.org/packages/87/53/e39d67fd3296b649772780ca6aab081412838ecb54e0b0c6432d01626a50/blis-1.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:45866a9027d43b93e8b59980a23c5d7358b6536fc04606286e39fdcfce1101c2", size = 14251222, upload-time = "2025-11-17T12:27:39.705Z" }, + { url = "https://files.pythonhosted.org/packages/ea/44/b749f8777b020b420bceaaf60f66432fc30cc904ca5b69640ec9cbef11ed/blis-1.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:27f82b8633030f8d095d2b412dffa7eb6dbc8ee43813139909a20012e54422ea", size = 6171233, upload-time = "2025-11-17T12:27:41.921Z" }, + { url = "https://files.pythonhosted.org/packages/16/d1/429cf0cf693d4c7dc2efed969bd474e315aab636e4a95f66c4ed7264912d/blis-1.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2a1c74e100665f8e918ebdbae2794576adf1f691680b5cdb8b29578432f623ef", size = 6929663, upload-time = "2025-11-17T12:27:44.482Z" }, + { url = "https://files.pythonhosted.org/packages/11/69/363c8df8d98b3cc97be19aad6aabb2c9c53f372490d79316bdee92d476e7/blis-1.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3f6c595185176ce021316263e1a1d636a3425b6c48366c1fd712d08d0b71849a", size = 1230939, upload-time = "2025-11-17T12:27:46.19Z" }, + { url = "https://files.pythonhosted.org/packages/96/2a/fbf65d906d823d839076c5150a6f8eb5ecbc5f9135e0b6510609bda1e6b7/blis-1.3.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d734b19fba0be7944f272dfa7b443b37c61f9476d9ab054a9ac53555ceadd2e0", size = 2818835, upload-time = "2025-11-17T12:27:48.167Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ad/58deaa3ad856dd3cc96493e40ffd2ed043d18d4d304f85a65cde1ccbf644/blis-1.3.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ef6d6e2b599a3a2788eb6d9b443533961265aa4ec49d574ed4bb846e548dcdb", size = 11366550, upload-time = "2025-11-17T12:27:49.958Z" }, + { url = "https://files.pythonhosted.org/packages/78/82/816a7adfe1f7acc8151f01ec86ef64467a3c833932d8f19f8e06613b8a4e/blis-1.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8c888438ae99c500422d50698e3028b65caa8ebb44e24204d87fda2df64058f7", size = 3023686, upload-time = "2025-11-17T12:27:52.062Z" }, + { url = "https://files.pythonhosted.org/packages/1e/e2/0e93b865f648b5519360846669a35f28ee8f4e1d93d054f6850d8afbabde/blis-1.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8177879fd3590b5eecdd377f9deafb5dc8af6d684f065bd01553302fb3fcf9a7", size = 14250939, upload-time = "2025-11-17T12:27:53.847Z" }, + { url = "https://files.pythonhosted.org/packages/20/07/fb43edc2ff0a6a367e4a94fc39eb3b85aa1e55e24cc857af2db145ce9f0d/blis-1.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:f20f7ad69aaffd1ce14fe77de557b6df9b61e0c9e582f75a843715d836b5c8af", size = 6192759, upload-time = "2025-11-17T12:27:56.176Z" }, +] + +[[package]] +name = "captum" +version = "0.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "matplotlib" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "torch" }, + { name = "tqdm" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/fa/a63083e09f7dc48a251a437980cf72efff6afc689f1ef9b23b6deaa1e684/captum-0.8.0.tar.gz", hash = "sha256:73f2e5a07ffe6ac6647a6f12cd2f67f0d8f717329f150ddf08ef73cba0858d26", size = 1303037, upload-time = "2025-03-27T04:48:10.044Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7d/2d/57138b29a53d1fd267c8093989f6ec64ef55d3c6bd336fac475239c61b97/captum-0.8.0-py3-none-any.whl", hash = "sha256:b0465431f5cbc666d86c14ee853ee6155e97460369238f85bd10a241b87eea47", size = 1403341, upload-time = "2025-03-27T04:48:08.056Z" }, +] + +[[package]] +name = "catalogue" +version = "2.0.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/38/b4/244d58127e1cdf04cf2dc7d9566f0d24ef01d5ce21811bab088ecc62b5ea/catalogue-2.0.10.tar.gz", hash = "sha256:4f56daa940913d3f09d589c191c74e5a6d51762b3a9e37dd53b7437afd6cda15", size = 19561, upload-time = "2023-09-25T06:29:24.962Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/96/d32b941a501ab566a16358d68b6eb4e4acc373fab3c3c4d7d9e649f7b4bb/catalogue-2.0.10-py3-none-any.whl", hash = "sha256:58c2de0020aa90f4a2da7dfad161bf7b3b054c86a5f09fcedc0b2b740c109a9f", size = 17325, upload-time = "2023-09-25T06:29:23.337Z" }, +] + +[[package]] +name = "catboost" +version = "1.2.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "graphviz" }, + { name = "matplotlib" }, + { name = "numpy" }, + { name = "pandas" }, + { name = "plotly" }, + { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scipy", version = "1.16.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0c/ee/8f146ee0b5c6321d4699edd90a036fe68b2c5fad910fa2b369f14043c192/catboost-1.2.8.tar.gz", hash = "sha256:4a1d1aca5caecd919ec476f72c7abd98a704c24fda35506d4d7d71f77f07cb29", size = 58080776, upload-time = "2025-04-13T10:14:19.057Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b0/3f/e2410e21fdd8a1c30eacd97a4203c0dad0ba35eb0d029bebd0dfc810e699/catboost-1.2.8-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:8409c8a2e547469070d73681aa615b5e0b0d78367203d201b2f2b25c33cdcbad", size = 27810638, upload-time = "2025-04-13T10:12:07.475Z" }, + { url = "https://files.pythonhosted.org/packages/39/22/53612f0c82a8c8ff60be1f734270dde3626bf2dd581d7ad753e2f3fadfc1/catboost-1.2.8-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:063020755d21de4f5434663a9b1d7cc1507c5b9254e2e8cd9cce9cd3b9ba4bbe", size = 98748971, upload-time = "2025-04-13T10:12:12.256Z" }, + { url = "https://files.pythonhosted.org/packages/1a/94/9c42fd69a7cfbcd3f599b2ce6bff0ca286779cd0f2068f94d51c0ff5dbd6/catboost-1.2.8-cp310-cp310-manylinux2014_x86_64.whl", hash = "sha256:5ac7c03a619d8eb86d70ec5748c1c9f09d4085033e3a760bf2f7c2892513c8a8", size = 99162172, upload-time = "2025-04-13T10:12:18.358Z" }, + { url = "https://files.pythonhosted.org/packages/4c/71/a05501a74e043b2c3ea5264dce8f5d55f0c84c91900581c93a947f258d64/catboost-1.2.8-cp310-cp310-win_amd64.whl", hash = "sha256:b661840dc65e6ab4e62484dbf1556fed7736bb9196c6b5a3abb003cea39f0f91", size = 102466099, upload-time = "2025-04-13T10:12:24.221Z" }, + { url = "https://files.pythonhosted.org/packages/ae/48/f1d7b4b37f9e56ce6b2c0471465d6877fb475e0ac9cf1bc463517b2f4a82/catboost-1.2.8-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:29526147f37aa356b94bd06513cf36ebcd5a83a9186cee25a8223b104c016b9b", size = 27824680, upload-time = "2025-04-13T10:12:28.856Z" }, + { url = "https://files.pythonhosted.org/packages/31/13/1d5f340f3b819b5c86d53a3677d98bfde879574e22e82957df212cf5c488/catboost-1.2.8-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:af140777a4062aabb4aed6731aee0737c6137dcdd5f7354b5d3b11033c1586ae", size = 98755521, upload-time = "2025-04-13T10:12:33.227Z" }, + { url = "https://files.pythonhosted.org/packages/e2/47/abee19aae4b2a2a21e40e3c09db784099d189b3a0745e59c1d152700d90a/catboost-1.2.8-cp311-cp311-manylinux2014_x86_64.whl", hash = "sha256:810e00c9b570d449ebb2406183b9e1f8b8ce275b4eedeba750b24f088932264b", size = 99171305, upload-time = "2025-04-13T10:12:39.238Z" }, + { url = "https://files.pythonhosted.org/packages/0e/91/e60d80ce72e5fce94fa672908b1f7ffb881701027130b7d637bb6b6561a4/catboost-1.2.8-cp311-cp311-win_amd64.whl", hash = "sha256:8985dd217fe79161b05ed251c5f8a18130e2330d5c77559ac91b99b0cf781e6b", size = 102465509, upload-time = "2025-04-13T10:12:45.427Z" }, + { url = "https://files.pythonhosted.org/packages/14/88/ebc0a95d92b9090e6b17a55ceda950d3cbd1ee545286798e8355590501a6/catboost-1.2.8-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:29f93b4a89ef807e74c16882623c89f1fb781346e1f4fafb29b6949ab4603e14", size = 27843240, upload-time = "2025-04-13T10:12:52.6Z" }, + { url = "https://files.pythonhosted.org/packages/be/d1/06142eecc68405b1ded7691fedc6639c6ae35b1d1d9e322ed45f6ee1ded3/catboost-1.2.8-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:932542f8b416b43ee07f912a9a964635ccca7397da16b61475c76ae4ae96a1df", size = 98726147, upload-time = "2025-04-13T10:12:57.038Z" }, + { url = "https://files.pythonhosted.org/packages/ce/39/22643f61f2b6526f5fe5985b4e3ea1596fc5c8dbe635cd88e8ebbd2dfcb7/catboost-1.2.8-cp312-cp312-manylinux2014_x86_64.whl", hash = "sha256:35a70d32809a21d06dc0ba161bafdd0450ea71fe176a12ee85d7535883b22624", size = 99167436, upload-time = "2025-04-13T10:13:05.31Z" }, + { url = "https://files.pythonhosted.org/packages/bd/9e/feae59f6226f742fa3fa30ae126e0941f443d460e7c0fa9f79cdf3ee488f/catboost-1.2.8-cp312-cp312-win_amd64.whl", hash = "sha256:319086796084fee5e4254300dc81aad1ae0b201cb576a9e87e6c7d030483be7e", size = 102425363, upload-time = "2025-04-13T10:13:10.826Z" }, +] + +[[package]] +name = "certifi" +version = "2022.12.7" +source = { registry = "https://download.pytorch.org/whl/cu126" } +wheels = [ + { url = "https://download.pytorch.org/whl/certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18" }, +] + +[[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" } +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" }, +] + +[[package]] +name = "charset-normalizer" +version = "2.1.1" +source = { registry = "https://download.pytorch.org/whl/cu126" } +wheels = [ + { url = "https://download.pytorch.org/whl/charset_normalizer-2.1.1-py3-none-any.whl", hash = "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f" }, +] + +[[package]] +name = "choreographer" +version = "1.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "logistro" }, + { name = "simplejson" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/74/47/64a035c6f764450ea9f902cbeba14c8c70316c2641125510066d8f912bfa/choreographer-1.2.1.tar.gz", hash = "sha256:022afd72b1e9b0bcb950420b134e70055a294c791b6f36cfb47d89745b701b5f", size = 43399, upload-time = "2025-11-09T23:04:44.749Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/9f/d73dfb85d7a5b1a56a99adc50f2074029468168c970ff5daeade4ad819e4/choreographer-1.2.1-py3-none-any.whl", hash = "sha256:9af5385effa3c204dbc337abf7ac74fd8908ced326a15645dc31dde75718c77e", size = 49338, upload-time = "2025-11-09T23:04:43.154Z" }, +] + +[[package]] +name = "click" +version = "8.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { 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, 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, upload-time = "2025-11-15T20:45:41.139Z" }, +] + +[[package]] +name = "cloudpathlib" +version = "0.23.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f4/18/2ac35d6b3015a0c74e923d94fc69baf8307f7c3233de015d69f99e17afa8/cloudpathlib-0.23.0.tar.gz", hash = "sha256:eb38a34c6b8a048ecfd2b2f60917f7cbad4a105b7c979196450c2f541f4d6b4b", size = 53126, upload-time = "2025-10-07T22:47:56.278Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ae/8a/c4bb04426d608be4a3171efa2e233d2c59a5c8937850c10d098e126df18e/cloudpathlib-0.23.0-py3-none-any.whl", hash = "sha256:8520b3b01468fee77de37ab5d50b1b524ea6b4a8731c35d1b7407ac0cd716002", size = 62755, upload-time = "2025-10-07T22:47:54.905Z" }, +] + +[[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" +source = { registry = "https://download.pytorch.org/whl/cu126" } +wheels = [ + { url = "https://download.pytorch.org/whl/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6" }, +] + +[[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 = "confection" +version = "0.1.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "srsly" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/51/d3/57c6631159a1b48d273b40865c315cf51f89df7a9d1101094ef12e3a37c2/confection-0.1.5.tar.gz", hash = "sha256:8e72dd3ca6bd4f48913cd220f10b8275978e740411654b6e8ca6d7008c590f0e", size = 38924, upload-time = "2024-05-31T16:17:01.559Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/00/3106b1854b45bd0474ced037dfe6b73b90fe68a68968cef47c23de3d43d2/confection-0.1.5-py3-none-any.whl", hash = "sha256:e29d3c3f8eac06b3f77eb9dfb4bf2fc6bcc9622a98ca00a698e3d019c6430b14", size = 35451, upload-time = "2024-05-31T16:16:59.075Z" }, +] + +[[package]] +name = "contourpy" +version = "1.3.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'linux'", + "python_full_version < '3.11' and sys_platform == 'darwin'", + "python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux'", +] +dependencies = [ + { name = "numpy", marker = "python_full_version < '3.11'" }, +] +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, 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/b3/b9/ede788a0b56fc5b071639d06c33cb893f68b1178938f3425debebe2dab78/contourpy-1.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6a37a2fb93d4df3fc4c0e363ea4d16f83195fc09c891bc8ce072b9d084853445", size = 269636, upload-time = "2025-04-15T17:35:54.473Z" }, + { url = "https://files.pythonhosted.org/packages/e6/75/3469f011d64b8bbfa04f709bfc23e1dd71be54d05b1b083be9f5b22750d1/contourpy-1.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b7cd50c38f500bbcc9b6a46643a40e0913673f869315d8e70de0438817cb7773", size = 254636, upload-time = "2025-04-15T17:35:58.283Z" }, + { url = "https://files.pythonhosted.org/packages/8d/2f/95adb8dae08ce0ebca4fd8e7ad653159565d9739128b2d5977806656fcd2/contourpy-1.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6658ccc7251a4433eebd89ed2672c2ed96fba367fd25ca9512aa92a4b46c4f1", size = 313053, upload-time = "2025-04-15T17:36:03.235Z" }, + { url = "https://files.pythonhosted.org/packages/c3/a6/8ccf97a50f31adfa36917707fe39c9a0cbc24b3bbb58185577f119736cc9/contourpy-1.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:70771a461aaeb335df14deb6c97439973d253ae70660ca085eec25241137ef43", size = 352985, upload-time = "2025-04-15T17:36:08.275Z" }, + { url = "https://files.pythonhosted.org/packages/1d/b6/7925ab9b77386143f39d9c3243fdd101621b4532eb126743201160ffa7e6/contourpy-1.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65a887a6e8c4cd0897507d814b14c54a8c2e2aa4ac9f7686292f9769fcf9a6ab", size = 323750, upload-time = "2025-04-15T17:36:13.29Z" }, + { url = "https://files.pythonhosted.org/packages/c2/f3/20c5d1ef4f4748e52d60771b8560cf00b69d5c6368b5c2e9311bcfa2a08b/contourpy-1.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3859783aefa2b8355697f16642695a5b9792e7a46ab86da1118a4a23a51a33d7", size = 326246, upload-time = "2025-04-15T17:36:18.329Z" }, + { url = "https://files.pythonhosted.org/packages/8c/e5/9dae809e7e0b2d9d70c52b3d24cba134dd3dad979eb3e5e71f5df22ed1f5/contourpy-1.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:eab0f6db315fa4d70f1d8ab514e527f0366ec021ff853d7ed6a2d33605cf4b83", size = 1308728, upload-time = "2025-04-15T17:36:33.878Z" }, + { url = "https://files.pythonhosted.org/packages/e2/4a/0058ba34aeea35c0b442ae61a4f4d4ca84d6df8f91309bc2d43bb8dd248f/contourpy-1.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d91a3ccc7fea94ca0acab82ceb77f396d50a1f67412efe4c526f5d20264e6ecd", size = 1375762, upload-time = "2025-04-15T17:36:51.295Z" }, + { url = "https://files.pythonhosted.org/packages/09/33/7174bdfc8b7767ef2c08ed81244762d93d5c579336fc0b51ca57b33d1b80/contourpy-1.3.2-cp311-cp311-win32.whl", hash = "sha256:1c48188778d4d2f3d48e4643fb15d8608b1d01e4b4d6b0548d9b336c28fc9b6f", size = 178196, upload-time = "2025-04-15T17:36:55.002Z" }, + { url = "https://files.pythonhosted.org/packages/5e/fe/4029038b4e1c4485cef18e480b0e2cd2d755448bb071eb9977caac80b77b/contourpy-1.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:5ebac872ba09cb8f2131c46b8739a7ff71de28a24c869bcad554477eb089a878", size = 222017, upload-time = "2025-04-15T17:36:58.576Z" }, + { url = "https://files.pythonhosted.org/packages/34/f7/44785876384eff370c251d58fd65f6ad7f39adce4a093c934d4a67a7c6b6/contourpy-1.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4caf2bcd2969402bf77edc4cb6034c7dd7c0803213b3523f111eb7460a51b8d2", size = 271580, upload-time = "2025-04-15T17:37:03.105Z" }, + { url = "https://files.pythonhosted.org/packages/93/3b/0004767622a9826ea3d95f0e9d98cd8729015768075d61f9fea8eeca42a8/contourpy-1.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:82199cb78276249796419fe36b7386bd8d2cc3f28b3bc19fe2454fe2e26c4c15", size = 255530, upload-time = "2025-04-15T17:37:07.026Z" }, + { url = "https://files.pythonhosted.org/packages/e7/bb/7bd49e1f4fa805772d9fd130e0d375554ebc771ed7172f48dfcd4ca61549/contourpy-1.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:106fab697af11456fcba3e352ad50effe493a90f893fca6c2ca5c033820cea92", size = 307688, upload-time = "2025-04-15T17:37:11.481Z" }, + { url = "https://files.pythonhosted.org/packages/fc/97/e1d5dbbfa170725ef78357a9a0edc996b09ae4af170927ba8ce977e60a5f/contourpy-1.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d14f12932a8d620e307f715857107b1d1845cc44fdb5da2bc8e850f5ceba9f87", size = 347331, upload-time = "2025-04-15T17:37:18.212Z" }, + { url = "https://files.pythonhosted.org/packages/6f/66/e69e6e904f5ecf6901be3dd16e7e54d41b6ec6ae3405a535286d4418ffb4/contourpy-1.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:532fd26e715560721bb0d5fc7610fce279b3699b018600ab999d1be895b09415", size = 318963, upload-time = "2025-04-15T17:37:22.76Z" }, + { url = "https://files.pythonhosted.org/packages/a8/32/b8a1c8965e4f72482ff2d1ac2cd670ce0b542f203c8e1d34e7c3e6925da7/contourpy-1.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26b383144cf2d2c29f01a1e8170f50dacf0eac02d64139dcd709a8ac4eb3cfe", size = 323681, upload-time = "2025-04-15T17:37:33.001Z" }, + { url = "https://files.pythonhosted.org/packages/30/c6/12a7e6811d08757c7162a541ca4c5c6a34c0f4e98ef2b338791093518e40/contourpy-1.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c49f73e61f1f774650a55d221803b101d966ca0c5a2d6d5e4320ec3997489441", size = 1308674, upload-time = "2025-04-15T17:37:48.64Z" }, + { url = "https://files.pythonhosted.org/packages/2a/8a/bebe5a3f68b484d3a2b8ffaf84704b3e343ef1addea528132ef148e22b3b/contourpy-1.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3d80b2c0300583228ac98d0a927a1ba6a2ba6b8a742463c564f1d419ee5b211e", size = 1380480, upload-time = "2025-04-15T17:38:06.7Z" }, + { url = "https://files.pythonhosted.org/packages/34/db/fcd325f19b5978fb509a7d55e06d99f5f856294c1991097534360b307cf1/contourpy-1.3.2-cp312-cp312-win32.whl", hash = "sha256:90df94c89a91b7362e1142cbee7568f86514412ab8a2c0d0fca72d7e91b62912", size = 178489, upload-time = "2025-04-15T17:38:10.338Z" }, + { url = "https://files.pythonhosted.org/packages/01/c8/fadd0b92ffa7b5eb5949bf340a63a4a496a6930a6c37a7ba0f12acb076d6/contourpy-1.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:8c942a01d9163e2e5cfb05cb66110121b8d07ad438a17f9e766317bcb62abf73", size = 223042, upload-time = "2025-04-15T17:38:14.239Z" }, + { 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/ff/c0/91f1215d0d9f9f343e4773ba6c9b89e8c0cc7a64a6263f21139da639d848/contourpy-1.3.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5f5964cdad279256c084b69c3f412b7801e15356b16efa9d78aa974041903da0", size = 266807, upload-time = "2025-04-15T17:45:15.535Z" }, + { url = "https://files.pythonhosted.org/packages/d4/79/6be7e90c955c0487e7712660d6cead01fa17bff98e0ea275737cc2bc8e71/contourpy-1.3.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49b65a95d642d4efa8f64ba12558fcb83407e58a2dfba9d796d77b63ccfcaff5", size = 318729, upload-time = "2025-04-15T17:45:20.166Z" }, + { url = "https://files.pythonhosted.org/packages/87/68/7f46fb537958e87427d98a4074bcde4b67a70b04900cfc5ce29bc2f556c1/contourpy-1.3.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:8c5acb8dddb0752bf252e01a3035b21443158910ac16a3b0d20e7fed7d534ce5", size = 221791, upload-time = "2025-04-15T17:45:24.794Z" }, +] + +[[package]] +name = "contourpy" +version = "1.3.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "python_full_version >= '3.12' and platform_machine != 'aarch64' and sys_platform == 'linux'", + "python_full_version >= '3.12' and sys_platform == 'darwin'", + "python_full_version >= '3.12' and sys_platform != 'darwin' and sys_platform != 'linux'", + "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "python_full_version == '3.11.*' and platform_machine != 'aarch64' and sys_platform == 'linux'", + "python_full_version == '3.11.*' and sys_platform == 'darwin'", + "python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux'", +] +dependencies = [ + { name = "numpy", marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880", size = 13466174, upload-time = "2025-07-26T12:03:12.549Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/2e/c4390a31919d8a78b90e8ecf87cd4b4c4f05a5b48d05ec17db8e5404c6f4/contourpy-1.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:709a48ef9a690e1343202916450bc48b9e51c049b089c7f79a267b46cffcdaa1", size = 288773, upload-time = "2025-07-26T12:01:02.277Z" }, + { url = "https://files.pythonhosted.org/packages/0d/44/c4b0b6095fef4dc9c420e041799591e3b63e9619e3044f7f4f6c21c0ab24/contourpy-1.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:23416f38bfd74d5d28ab8429cc4d63fa67d5068bd711a85edb1c3fb0c3e2f381", size = 270149, upload-time = "2025-07-26T12:01:04.072Z" }, + { url = "https://files.pythonhosted.org/packages/30/2e/dd4ced42fefac8470661d7cb7e264808425e6c5d56d175291e93890cce09/contourpy-1.3.3-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:929ddf8c4c7f348e4c0a5a3a714b5c8542ffaa8c22954862a46ca1813b667ee7", size = 329222, upload-time = "2025-07-26T12:01:05.688Z" }, + { url = "https://files.pythonhosted.org/packages/f2/74/cc6ec2548e3d276c71389ea4802a774b7aa3558223b7bade3f25787fafc2/contourpy-1.3.3-cp311-cp311-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9e999574eddae35f1312c2b4b717b7885d4edd6cb46700e04f7f02db454e67c1", size = 377234, upload-time = "2025-07-26T12:01:07.054Z" }, + { url = "https://files.pythonhosted.org/packages/03/b3/64ef723029f917410f75c09da54254c5f9ea90ef89b143ccadb09df14c15/contourpy-1.3.3-cp311-cp311-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0bf67e0e3f482cb69779dd3061b534eb35ac9b17f163d851e2a547d56dba0a3a", size = 380555, upload-time = "2025-07-26T12:01:08.801Z" }, + { url = "https://files.pythonhosted.org/packages/5f/4b/6157f24ca425b89fe2eb7e7be642375711ab671135be21e6faa100f7448c/contourpy-1.3.3-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:51e79c1f7470158e838808d4a996fa9bac72c498e93d8ebe5119bc1e6becb0db", size = 355238, upload-time = "2025-07-26T12:01:10.319Z" }, + { url = "https://files.pythonhosted.org/packages/98/56/f914f0dd678480708a04cfd2206e7c382533249bc5001eb9f58aa693e200/contourpy-1.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:598c3aaece21c503615fd59c92a3598b428b2f01bfb4b8ca9c4edeecc2438620", size = 1326218, upload-time = "2025-07-26T12:01:12.659Z" }, + { url = "https://files.pythonhosted.org/packages/fb/d7/4a972334a0c971acd5172389671113ae82aa7527073980c38d5868ff1161/contourpy-1.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:322ab1c99b008dad206d406bb61d014cf0174df491ae9d9d0fac6a6fda4f977f", size = 1392867, upload-time = "2025-07-26T12:01:15.533Z" }, + { url = "https://files.pythonhosted.org/packages/75/3e/f2cc6cd56dc8cff46b1a56232eabc6feea52720083ea71ab15523daab796/contourpy-1.3.3-cp311-cp311-win32.whl", hash = "sha256:fd907ae12cd483cd83e414b12941c632a969171bf90fc937d0c9f268a31cafff", size = 183677, upload-time = "2025-07-26T12:01:17.088Z" }, + { url = "https://files.pythonhosted.org/packages/98/4b/9bd370b004b5c9d8045c6c33cf65bae018b27aca550a3f657cdc99acdbd8/contourpy-1.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:3519428f6be58431c56581f1694ba8e50626f2dd550af225f82fb5f5814d2a42", size = 225234, upload-time = "2025-07-26T12:01:18.256Z" }, + { url = "https://files.pythonhosted.org/packages/d9/b6/71771e02c2e004450c12b1120a5f488cad2e4d5b590b1af8bad060360fe4/contourpy-1.3.3-cp311-cp311-win_arm64.whl", hash = "sha256:15ff10bfada4bf92ec8b31c62bf7c1834c244019b4a33095a68000d7075df470", size = 193123, upload-time = "2025-07-26T12:01:19.848Z" }, + { url = "https://files.pythonhosted.org/packages/be/45/adfee365d9ea3d853550b2e735f9d66366701c65db7855cd07621732ccfc/contourpy-1.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b08a32ea2f8e42cf1d4be3169a98dd4be32bafe4f22b6c4cb4ba810fa9e5d2cb", size = 293419, upload-time = "2025-07-26T12:01:21.16Z" }, + { url = "https://files.pythonhosted.org/packages/53/3e/405b59cfa13021a56bba395a6b3aca8cec012b45bf177b0eaf7a202cde2c/contourpy-1.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:556dba8fb6f5d8742f2923fe9457dbdd51e1049c4a43fd3986a0b14a1d815fc6", size = 273979, upload-time = "2025-07-26T12:01:22.448Z" }, + { url = "https://files.pythonhosted.org/packages/d4/1c/a12359b9b2ca3a845e8f7f9ac08bdf776114eb931392fcad91743e2ea17b/contourpy-1.3.3-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92d9abc807cf7d0e047b95ca5d957cf4792fcd04e920ca70d48add15c1a90ea7", size = 332653, upload-time = "2025-07-26T12:01:24.155Z" }, + { url = "https://files.pythonhosted.org/packages/63/12/897aeebfb475b7748ea67b61e045accdfcf0d971f8a588b67108ed7f5512/contourpy-1.3.3-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b2e8faa0ed68cb29af51edd8e24798bb661eac3bd9f65420c1887b6ca89987c8", size = 379536, upload-time = "2025-07-26T12:01:25.91Z" }, + { url = "https://files.pythonhosted.org/packages/43/8a/a8c584b82deb248930ce069e71576fc09bd7174bbd35183b7943fb1064fd/contourpy-1.3.3-cp312-cp312-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:626d60935cf668e70a5ce6ff184fd713e9683fb458898e4249b63be9e28286ea", size = 384397, upload-time = "2025-07-26T12:01:27.152Z" }, + { url = "https://files.pythonhosted.org/packages/cc/8f/ec6289987824b29529d0dfda0d74a07cec60e54b9c92f3c9da4c0ac732de/contourpy-1.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d00e655fcef08aba35ec9610536bfe90267d7ab5ba944f7032549c55a146da1", size = 362601, upload-time = "2025-07-26T12:01:28.808Z" }, + { url = "https://files.pythonhosted.org/packages/05/0a/a3fe3be3ee2dceb3e615ebb4df97ae6f3828aa915d3e10549ce016302bd1/contourpy-1.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:451e71b5a7d597379ef572de31eeb909a87246974d960049a9848c3bc6c41bf7", size = 1331288, upload-time = "2025-07-26T12:01:31.198Z" }, + { url = "https://files.pythonhosted.org/packages/33/1d/acad9bd4e97f13f3e2b18a3977fe1b4a37ecf3d38d815333980c6c72e963/contourpy-1.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:459c1f020cd59fcfe6650180678a9993932d80d44ccde1fa1868977438f0b411", size = 1403386, upload-time = "2025-07-26T12:01:33.947Z" }, + { url = "https://files.pythonhosted.org/packages/cf/8f/5847f44a7fddf859704217a99a23a4f6417b10e5ab1256a179264561540e/contourpy-1.3.3-cp312-cp312-win32.whl", hash = "sha256:023b44101dfe49d7d53932be418477dba359649246075c996866106da069af69", size = 185018, upload-time = "2025-07-26T12:01:35.64Z" }, + { url = "https://files.pythonhosted.org/packages/19/e8/6026ed58a64563186a9ee3f29f41261fd1828f527dd93d33b60feca63352/contourpy-1.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:8153b8bfc11e1e4d75bcb0bff1db232f9e10b274e0929de9d608027e0d34ff8b", size = 226567, upload-time = "2025-07-26T12:01:36.804Z" }, + { url = "https://files.pythonhosted.org/packages/d1/e2/f05240d2c39a1ed228d8328a78b6f44cd695f7ef47beb3e684cf93604f86/contourpy-1.3.3-cp312-cp312-win_arm64.whl", hash = "sha256:07ce5ed73ecdc4a03ffe3e1b3e3c1166db35ae7584be76f65dbbe28a7791b0cc", size = 193655, upload-time = "2025-07-26T12:01:37.999Z" }, + { url = "https://files.pythonhosted.org/packages/a5/29/8dcfe16f0107943fa92388c23f6e05cff0ba58058c4c95b00280d4c75a14/contourpy-1.3.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:cd5dfcaeb10f7b7f9dc8941717c6c2ade08f587be2226222c12b25f0483ed497", size = 278809, upload-time = "2025-07-26T12:02:52.74Z" }, + { url = "https://files.pythonhosted.org/packages/85/a9/8b37ef4f7dafeb335daee3c8254645ef5725be4d9c6aa70b50ec46ef2f7e/contourpy-1.3.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:0c1fc238306b35f246d61a1d416a627348b5cf0648648a031e14bb8705fcdfe8", size = 261593, upload-time = "2025-07-26T12:02:54.037Z" }, + { url = "https://files.pythonhosted.org/packages/0a/59/ebfb8c677c75605cc27f7122c90313fd2f375ff3c8d19a1694bda74aaa63/contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70f9aad7de812d6541d29d2bbf8feb22ff7e1c299523db288004e3157ff4674e", size = 302202, upload-time = "2025-07-26T12:02:55.947Z" }, + { url = "https://files.pythonhosted.org/packages/3c/37/21972a15834d90bfbfb009b9d004779bd5a07a0ec0234e5ba8f64d5736f4/contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5ed3657edf08512fc3fe81b510e35c2012fbd3081d2e26160f27ca28affec989", size = 329207, upload-time = "2025-07-26T12:02:57.468Z" }, + { url = "https://files.pythonhosted.org/packages/0c/58/bd257695f39d05594ca4ad60df5bcb7e32247f9951fd09a9b8edb82d1daa/contourpy-1.3.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:3d1a3799d62d45c18bafd41c5fa05120b96a28079f2393af559b843d1a966a77", size = 225315, upload-time = "2025-07-26T12:02:58.801Z" }, +] + +[[package]] +name = "cuda-bindings" +version = "12.9.4" +source = { registry = "https://download.pytorch.org/whl/cu126" } +dependencies = [ + { name = "cuda-pathfinder", marker = "sys_platform == 'linux'" }, +] +wheels = [ + { url = "https://download.pytorch.org/whl/cu126/cuda_bindings-12.9.4-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a022c96b8bd847e8dc0675523431149a4c3e872f440e3002213dbb9e08f0331a" }, + { url = "https://download.pytorch.org/whl/cu126/cuda_bindings-12.9.4-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d3c842c2a4303b2a580fe955018e31aea30278be19795ae05226235268032e5" }, + { url = "https://download.pytorch.org/whl/cu126/cuda_bindings-12.9.4-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6a429dc6c13148ff1e27c44f40a3dd23203823e637b87fd0854205195988306" }, + { url = "https://download.pytorch.org/whl/cu126/cuda_bindings-12.9.4-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c912a3d9e6b6651853eed8eed96d6800d69c08e94052c292fec3f282c5a817c9" }, + { url = "https://download.pytorch.org/whl/cu126/cuda_bindings-12.9.4-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:694ba35023846625ef471257e6b5a4bc8af690f961d197d77d34b1d1db393f56" }, + { url = "https://download.pytorch.org/whl/cu126/cuda_bindings-12.9.4-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fda147a344e8eaeca0c6ff113d2851ffca8f7dfc0a6c932374ee5c47caa649c8" }, +] + +[[package]] +name = "cuda-pathfinder" +version = "1.2.2" +source = { registry = "https://download.pytorch.org/whl/cu126" } +wheels = [ + { url = "https://download.pytorch.org/whl/cuda_pathfinder-1.2.2-py3-none-any.whl" }, +] + +[[package]] +name = "cuda-toolkit" +version = "12.6.3" +source = { registry = "https://download.pytorch.org/whl/cu126" } +wheels = [ + { url = "https://pypi.nvidia.com/cuda-toolkit/cuda_toolkit-12.6.3-py2.py3-none-any.whl", hash = "sha256:79d8605baeb6c2f695761e0efb54bc62dbc3c9e32eb0742df7669c07befaa8f7" }, +] + +[package.optional-dependencies] +cublas = [ + { name = "nvidia-cublas-cu12", marker = "sys_platform == 'linux'" }, +] +cudart = [ + { name = "nvidia-cuda-runtime-cu12", marker = "sys_platform == 'linux'" }, +] +cufft = [ + { name = "nvidia-cufft-cu12", marker = "sys_platform == 'linux'" }, +] +cufile = [ + { name = "nvidia-cufile-cu12", marker = "sys_platform == 'linux'" }, +] +cupti = [ + { name = "nvidia-cuda-cupti-cu12", marker = "sys_platform == 'linux'" }, +] +curand = [ + { name = "nvidia-curand-cu12", marker = "sys_platform == 'linux'" }, +] +cusolver = [ + { name = "nvidia-cusolver-cu12", marker = "sys_platform == 'linux'" }, +] +cusparse = [ + { name = "nvidia-cusparse-cu12", marker = "sys_platform == 'linux'" }, +] +nvjitlink = [ + { name = "nvidia-nvjitlink-cu12", marker = "sys_platform == 'linux'" }, +] +nvrtc = [ + { name = "nvidia-cuda-nvrtc-cu12", marker = "sys_platform == 'linux'" }, +] +nvtx = [ + { name = "nvidia-nvtx-cu12", marker = "sys_platform == 'linux'" }, +] + +[[package]] +name = "cycler" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615, upload-time = "2023-10-07T05:32:18.335Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321, upload-time = "2023-10-07T05:32:16.783Z" }, +] + +[[package]] +name = "cymem" +version = "2.0.13" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/2f0fbb32535c3731b7c2974c569fb9325e0a38ed5565a08e1139a3b71e82/cymem-2.0.13.tar.gz", hash = "sha256:1c91a92ae8c7104275ac26bd4d29b08ccd3e7faff5893d3858cb6fadf1bc1588", size = 12320, upload-time = "2025-11-14T14:58:36.902Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/14/462018dd384ee1848ac9c1951534a813a325abbfc161a74e2cbcb38d2469/cymem-2.0.13-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8efc4f308169237aade0e82877a65a563833dec32eb7ab2326120253e0e9e918", size = 43747, upload-time = "2025-11-14T14:57:11.287Z" }, + { url = "https://files.pythonhosted.org/packages/4b/9b/c123ba65dddcd8a2bc0b3c9046766c15abe0e257c315b3040eed22cce1e2/cymem-2.0.13-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e03bb575a96c59bc210d7d59862747f0012696b0dac3427ce8af33c7afb3d4a2", size = 43328, upload-time = "2025-11-14T14:57:12.578Z" }, + { url = "https://files.pythonhosted.org/packages/bd/be/7b7a4cf9cd2d37e674612a86fc90b3d59bff12177f83430e62b25afaf7fc/cymem-2.0.13-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1775d3fd34cf099929b79c3e48469283642463f977af6801231f3c0e5d9c9369", size = 231539, upload-time = "2025-11-14T14:57:14.441Z" }, + { url = "https://files.pythonhosted.org/packages/79/6d/d165c38cd4caaaf60942e2cec9998b667008f2384047ccfe0b4b5f7a1ffe/cymem-2.0.13-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:84e2976e38cd663f758e40b5497fa5cd183d7c5fb0d04ce81a4b42a1ba124ff0", size = 229674, upload-time = "2025-11-14T14:57:15.685Z" }, + { url = "https://files.pythonhosted.org/packages/95/c1/af83c03a93f890ca81149561b18a4a67a9aa36a1109f15e291dd2703ab12/cymem-2.0.13-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ed9de1b9b042f76fe5c312e4359eab58bf52ac7dfdf6887368a760410d809440", size = 229805, upload-time = "2025-11-14T14:57:17.289Z" }, + { url = "https://files.pythonhosted.org/packages/03/2d/12900758b80345d9aed5892a9d61e8a5f6abbbe5837e4def373a53cd0da2/cymem-2.0.13-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1366c7437a209230f4b797fae10227a8206d4021d37c9f9c0d31fd97ea4feb35", size = 234018, upload-time = "2025-11-14T14:57:18.512Z" }, + { url = "https://files.pythonhosted.org/packages/a6/8b/5fcf5430fc81098aef58cc20340e51f37b49b9d8c15766e0d5d63e7288a3/cymem-2.0.13-cp310-cp310-win_amd64.whl", hash = "sha256:7700b116524b087e0169f10f267539223b48240ef2734c3a727a9e6b4db9a671", size = 40102, upload-time = "2025-11-14T14:57:19.972Z" }, + { url = "https://files.pythonhosted.org/packages/0d/d3/cb6c83758fe399443b858faafb7096b72535621a7af7dd9a54ff0989fa14/cymem-2.0.13-cp310-cp310-win_arm64.whl", hash = "sha256:c8dbfddfe5c604974e17c6f373cedd4d25cd67f84812ede7dea12128fa0c2015", size = 36282, upload-time = "2025-11-14T14:57:21.398Z" }, + { url = "https://files.pythonhosted.org/packages/10/64/1db41f7576a6b69f70367e3c15e968fd775ba7419e12059c9966ceb826f8/cymem-2.0.13-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:673183466b0ff2e060d97ec5116711d44200b8f7be524323e080d215ee2d44a5", size = 43587, upload-time = "2025-11-14T14:57:22.39Z" }, + { url = "https://files.pythonhosted.org/packages/81/13/57f936fc08551323aab3f92ff6b7f4d4b89d5b4e495c870a67cb8d279757/cymem-2.0.13-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bee2791b3f6fc034ce41268851462bf662ff87e8947e35fb6dd0115b4644a61f", size = 43139, upload-time = "2025-11-14T14:57:23.363Z" }, + { url = "https://files.pythonhosted.org/packages/32/a6/9345754be51e0479aa387b7b6cffc289d0fd3201aaeb8dade4623abd1e02/cymem-2.0.13-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f3aee3adf16272bca81c5826eed55ba3c938add6d8c9e273f01c6b829ecfde22", size = 245063, upload-time = "2025-11-14T14:57:24.839Z" }, + { url = "https://files.pythonhosted.org/packages/d6/01/6bc654101526fa86e82bf6b05d99b2cd47c30a333cfe8622c26c0592beb2/cymem-2.0.13-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:30c4e75a3a1d809e89106b0b21803eb78e839881aa1f5b9bd27b454bc73afde3", size = 244496, upload-time = "2025-11-14T14:57:26.42Z" }, + { url = "https://files.pythonhosted.org/packages/c4/fb/853b7b021e701a1f41687f3704d5f469aeb2a4f898c3fbb8076806885955/cymem-2.0.13-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ec99efa03cf8ec11c8906aa4d4cc0c47df393bc9095c9dd64b89b9b43e220b04", size = 243287, upload-time = "2025-11-14T14:57:27.542Z" }, + { url = "https://files.pythonhosted.org/packages/d4/2b/0e4664cafc581de2896d75000651fd2ce7094d33263f466185c28ffc96e4/cymem-2.0.13-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c90a6ecba994a15b17a3f45d7ec74d34081df2f73bd1b090e2adc0317e4e01b6", size = 248287, upload-time = "2025-11-14T14:57:29.055Z" }, + { url = "https://files.pythonhosted.org/packages/21/0f/f94c6950edbfc2aafb81194fc40b6cacc8e994e9359d3cb4328c5705b9b5/cymem-2.0.13-cp311-cp311-win_amd64.whl", hash = "sha256:ce821e6ba59148ed17c4567113b8683a6a0be9c9ac86f14e969919121efb61a5", size = 40116, upload-time = "2025-11-14T14:57:30.592Z" }, + { url = "https://files.pythonhosted.org/packages/00/df/2455eff6ac0381ff165db6883b311f7016e222e3dd62185517f8e8187ed0/cymem-2.0.13-cp311-cp311-win_arm64.whl", hash = "sha256:0dca715e708e545fd1d97693542378a00394b20a37779c1ae2c8bdbb43acef79", size = 36349, upload-time = "2025-11-14T14:57:31.573Z" }, + { url = "https://files.pythonhosted.org/packages/c9/52/478a2911ab5028cb710b4900d64aceba6f4f882fcb13fd8d40a456a1b6dc/cymem-2.0.13-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e8afbc5162a0fe14b6463e1c4e45248a1b2fe2cbcecc8a5b9e511117080da0eb", size = 43745, upload-time = "2025-11-14T14:57:32.52Z" }, + { url = "https://files.pythonhosted.org/packages/f9/71/f0f8adee945524774b16af326bd314a14a478ed369a728a22834e6785a18/cymem-2.0.13-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9251d889348fe79a75e9b3e4d1b5fa651fca8a64500820685d73a3acc21b6a8", size = 42927, upload-time = "2025-11-14T14:57:33.827Z" }, + { url = "https://files.pythonhosted.org/packages/62/6d/159780fe162ff715d62b809246e5fc20901cef87ca28b67d255a8d741861/cymem-2.0.13-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:742fc19764467a49ed22e56a4d2134c262d73a6c635409584ae3bf9afa092c33", size = 258346, upload-time = "2025-11-14T14:57:34.917Z" }, + { url = "https://files.pythonhosted.org/packages/eb/12/678d16f7aa1996f947bf17b8cfb917ea9c9674ef5e2bd3690c04123d5680/cymem-2.0.13-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f190a92fe46197ee64d32560eb121c2809bb843341733227f51538ce77b3410d", size = 260843, upload-time = "2025-11-14T14:57:36.503Z" }, + { url = "https://files.pythonhosted.org/packages/31/5d/0dd8c167c08cd85e70d274b7235cfe1e31b3cebc99221178eaf4bbb95c6f/cymem-2.0.13-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d670329ee8dbbbf241b7c08069fe3f1d3a1a3e2d69c7d05ea008a7010d826298", size = 254607, upload-time = "2025-11-14T14:57:38.036Z" }, + { url = "https://files.pythonhosted.org/packages/b7/c9/d6514a412a1160aa65db539836b3d47f9b59f6675f294ec34ae32f867c82/cymem-2.0.13-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a84ba3178d9128b9ffb52ce81ebab456e9fe959125b51109f5b73ebdfc6b60d6", size = 262421, upload-time = "2025-11-14T14:57:39.265Z" }, + { url = "https://files.pythonhosted.org/packages/dd/fe/3ee37d02ca4040f2fb22d34eb415198f955862b5dd47eee01df4c8f5454c/cymem-2.0.13-cp312-cp312-win_amd64.whl", hash = "sha256:2ff1c41fd59b789579fdace78aa587c5fc091991fa59458c382b116fc36e30dc", size = 40176, upload-time = "2025-11-14T14:57:40.706Z" }, + { url = "https://files.pythonhosted.org/packages/94/fb/1b681635bfd5f2274d0caa8f934b58435db6c091b97f5593738065ddb786/cymem-2.0.13-cp312-cp312-win_arm64.whl", hash = "sha256:6bbd701338df7bf408648191dff52472a9b334f71bcd31a21a41d83821050f67", size = 35959, upload-time = "2025-11-14T14:57:41.682Z" }, +] + +[[package]] +name = "deepdiff" +version = "8.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "orderly-set" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/76/36c9aab3d5c19a94091f7c6c6e784efca50d87b124bf026c36e94719f33c/deepdiff-8.6.1.tar.gz", hash = "sha256:ec56d7a769ca80891b5200ec7bd41eec300ced91ebcc7797b41eb2b3f3ff643a", size = 634054, upload-time = "2025-09-03T19:40:41.461Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/e6/efe534ef0952b531b630780e19cabd416e2032697019d5295defc6ef9bd9/deepdiff-8.6.1-py3-none-any.whl", hash = "sha256:ee8708a7f7d37fb273a541fa24ad010ed484192cd0c4ffc0fa0ed5e2d4b9e78b", size = 91378, upload-time = "2025-09-03T19:40:39.679Z" }, +] + +[[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" } +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 = "docker-pycreds" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c5/e6/d1f6c00b7221e2d7c4b470132c931325c8b22c51ca62417e300f5ce16009/docker-pycreds-0.4.0.tar.gz", hash = "sha256:6ce3270bcaf404cc4c3e27e4b6c70d3521deae82fb508767870fdbf772d584d4", size = 8754, upload-time = "2018-11-29T03:26:50.996Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f5/e8/f6bd1eee09314e7e6dee49cbe2c5e22314ccdb38db16c9fc72d2fa80d054/docker_pycreds-0.4.0-py2.py3-none-any.whl", hash = "sha256:7266112468627868005106ec19cd0d722702d2b7d5912a28e19b826c3d37af49", size = 8982, upload-time = "2018-11-29T03:26:49.575Z" }, +] + +[[package]] +name = "elastic-transport" +version = "9.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "sniffio" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a9/0c/b48a9041e921a75e4c0d17e0dfe929f4d72c4e523084cbe5574101b7a40d/elastic_transport-9.2.0.tar.gz", hash = "sha256:0331466ca8febdb7d168c0fbf159294b0066492733b51da94f4dd28a0ee596cd", size = 77332, upload-time = "2025-10-17T16:12:49.906Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/36/4a/6a1cc529ec609dae4d681baddde9ad965b8952d1f56dadbb1d27fbb50ec9/elastic_transport-9.2.0-py3-none-any.whl", hash = "sha256:f52b961e58e6b76d488993286907f61a6ddccbdae8e0135ce8d369227b6282d8", size = 65317, upload-time = "2025-10-17T16:12:48.437Z" }, +] + +[[package]] +name = "elasticsearch" +version = "9.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "elastic-transport" }, + { name = "python-dateutil" }, + { name = "sniffio" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7c/87/6e70b8dac9c781c53edba944b50545431e2bb937d21c06d6009ef18b5f91/elasticsearch-9.2.0.tar.gz", hash = "sha256:99656baa34b10130402b6c38429feddf7aee1c3247566e8f95f0d0267fe9f9ab", size = 875370, upload-time = "2025-10-28T16:57:25.437Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/21/f2/6b44ee99e90c9d9e183aafeea0c3af87176d4acef446c286223704ea8f2e/elasticsearch-9.2.0-py3-none-any.whl", hash = "sha256:87090fe98c515ec0fce82f633fe11d7e90e04d93581b6b3e05de29efe4cc8b74", size = 960522, upload-time = "2025-10-28T16:57:20.979Z" }, +] + +[[package]] +name = "ensemble-boxes" +version = "1.0.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numba" }, + { name = "numpy" }, + { name = "pandas" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4e/d4/d313c9de69f1d628fe66e73b44c876ce5db250c7ac41fb7c1ed14644198a/ensemble_boxes-1.0.9.tar.gz", hash = "sha256:16a68101cb11606daac861e0e30f27e9748487e8166053dbf0a214db497ed4ae", size = 9942, upload-time = "2022-04-20T21:10:53.48Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/5b/58e47cd45fc18da37205a80689606e0e203810f1beadbdaae620f491892b/ensemble_boxes-1.0.9-py3-none-any.whl", hash = "sha256:f095f34d28034213791b8793b9de4d8ba6e40e6a2a6af02336b84c485d853d6a", size = 23897, upload-time = "2022-04-20T21:10:51.368Z" }, +] + +[[package]] +name = "exceptiongroup" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +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, upload-time = "2025-11-21T23:01:53.443Z" }, +] + +[[package]] +name = "filelock" +version = "3.20.0" +source = { registry = "https://download.pytorch.org/whl/cu126" } +sdist = { url = "https://files.pythonhosted.org/packages/58/46/0028a82567109b5ef6e4d2a1f04a583fb513e6cf9527fcdd09afd817deeb/filelock-3.20.0.tar.gz", hash = "sha256:711e943b4ec6be42e1d4e6690b48dc175c822967466bb31c0c293f34334c13f4" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/91/7216b27286936c16f5b4d0c530087e4a54eead683e6b0b73dd0c64844af6/filelock-3.20.0-py3-none-any.whl", hash = "sha256:339b4732ffda5cd79b13f4e2711a31b0365ce445d95d243bb996273d072546a2" }, +] + +[[package]] +name = "fonttools" +version = "4.60.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4b/42/97a13e47a1e51a5a7142475bbcf5107fe3a68fc34aef331c897d5fb98ad0/fonttools-4.60.1.tar.gz", hash = "sha256:ef00af0439ebfee806b25f24c8f92109157ff3fac5731dc7867957812e87b8d9", size = 3559823, upload-time = "2025-09-29T21:13:27.129Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/70/03e9d89a053caff6ae46053890eba8e4a5665a7c5638279ed4492e6d4b8b/fonttools-4.60.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9a52f254ce051e196b8fe2af4634c2d2f02c981756c6464dc192f1b6050b4e28", size = 2810747, upload-time = "2025-09-29T21:10:59.653Z" }, + { url = "https://files.pythonhosted.org/packages/6f/41/449ad5aff9670ab0df0f61ee593906b67a36d7e0b4d0cd7fa41ac0325bf5/fonttools-4.60.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c7420a2696a44650120cdd269a5d2e56a477e2bfa9d95e86229059beb1c19e15", size = 2346909, upload-time = "2025-09-29T21:11:02.882Z" }, + { url = "https://files.pythonhosted.org/packages/9a/18/e5970aa96c8fad1cb19a9479cc3b7602c0c98d250fcdc06a5da994309c50/fonttools-4.60.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee0c0b3b35b34f782afc673d503167157094a16f442ace7c6c5e0ca80b08f50c", size = 4864572, upload-time = "2025-09-29T21:11:05.096Z" }, + { url = "https://files.pythonhosted.org/packages/ce/20/9b2b4051b6ec6689480787d506b5003f72648f50972a92d04527a456192c/fonttools-4.60.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:282dafa55f9659e8999110bd8ed422ebe1c8aecd0dc396550b038e6c9a08b8ea", size = 4794635, upload-time = "2025-09-29T21:11:08.651Z" }, + { url = "https://files.pythonhosted.org/packages/10/52/c791f57347c1be98f8345e3dca4ac483eb97666dd7c47f3059aeffab8b59/fonttools-4.60.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4ba4bd646e86de16160f0fb72e31c3b9b7d0721c3e5b26b9fa2fc931dfdb2652", size = 4843878, upload-time = "2025-09-29T21:11:10.893Z" }, + { url = "https://files.pythonhosted.org/packages/69/e9/35c24a8d01644cee8c090a22fad34d5b61d1e0a8ecbc9945ad785ebf2e9e/fonttools-4.60.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0b0835ed15dd5b40d726bb61c846a688f5b4ce2208ec68779bc81860adb5851a", size = 4954555, upload-time = "2025-09-29T21:11:13.24Z" }, + { url = "https://files.pythonhosted.org/packages/f7/86/fb1e994971be4bdfe3a307de6373ef69a9df83fb66e3faa9c8114893d4cc/fonttools-4.60.1-cp310-cp310-win32.whl", hash = "sha256:1525796c3ffe27bb6268ed2a1bb0dcf214d561dfaf04728abf01489eb5339dce", size = 2232019, upload-time = "2025-09-29T21:11:15.73Z" }, + { url = "https://files.pythonhosted.org/packages/40/84/62a19e2bd56f0e9fb347486a5b26376bade4bf6bbba64dda2c103bd08c94/fonttools-4.60.1-cp310-cp310-win_amd64.whl", hash = "sha256:268ecda8ca6cb5c4f044b1fb9b3b376e8cd1b361cef275082429dc4174907038", size = 2276803, upload-time = "2025-09-29T21:11:18.152Z" }, + { url = "https://files.pythonhosted.org/packages/ea/85/639aa9bface1537e0fb0f643690672dde0695a5bbbc90736bc571b0b1941/fonttools-4.60.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7b4c32e232a71f63a5d00259ca3d88345ce2a43295bb049d21061f338124246f", size = 2831872, upload-time = "2025-09-29T21:11:20.329Z" }, + { url = "https://files.pythonhosted.org/packages/6b/47/3c63158459c95093be9618794acb1067b3f4d30dcc5c3e8114b70e67a092/fonttools-4.60.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3630e86c484263eaac71d117085d509cbcf7b18f677906824e4bace598fb70d2", size = 2356990, upload-time = "2025-09-29T21:11:22.754Z" }, + { url = "https://files.pythonhosted.org/packages/94/dd/1934b537c86fcf99f9761823f1fc37a98fbd54568e8e613f29a90fed95a9/fonttools-4.60.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5c1015318e4fec75dd4943ad5f6a206d9727adf97410d58b7e32ab644a807914", size = 5042189, upload-time = "2025-09-29T21:11:25.061Z" }, + { url = "https://files.pythonhosted.org/packages/d2/d2/9f4e4c4374dd1daa8367784e1bd910f18ba886db1d6b825b12edf6db3edc/fonttools-4.60.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e6c58beb17380f7c2ea181ea11e7db8c0ceb474c9dd45f48e71e2cb577d146a1", size = 4978683, upload-time = "2025-09-29T21:11:27.693Z" }, + { url = "https://files.pythonhosted.org/packages/cc/c4/0fb2dfd1ecbe9a07954cc13414713ed1eab17b1c0214ef07fc93df234a47/fonttools-4.60.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ec3681a0cb34c255d76dd9d865a55f260164adb9fa02628415cdc2d43ee2c05d", size = 5021372, upload-time = "2025-09-29T21:11:30.257Z" }, + { url = "https://files.pythonhosted.org/packages/0c/d5/495fc7ae2fab20223cc87179a8f50f40f9a6f821f271ba8301ae12bb580f/fonttools-4.60.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f4b5c37a5f40e4d733d3bbaaef082149bee5a5ea3156a785ff64d949bd1353fa", size = 5132562, upload-time = "2025-09-29T21:11:32.737Z" }, + { url = "https://files.pythonhosted.org/packages/bc/fa/021dab618526323c744e0206b3f5c8596a2e7ae9aa38db5948a131123e83/fonttools-4.60.1-cp311-cp311-win32.whl", hash = "sha256:398447f3d8c0c786cbf1209711e79080a40761eb44b27cdafffb48f52bcec258", size = 2230288, upload-time = "2025-09-29T21:11:35.015Z" }, + { url = "https://files.pythonhosted.org/packages/bb/78/0e1a6d22b427579ea5c8273e1c07def2f325b977faaf60bb7ddc01456cb1/fonttools-4.60.1-cp311-cp311-win_amd64.whl", hash = "sha256:d066ea419f719ed87bc2c99a4a4bfd77c2e5949cb724588b9dd58f3fd90b92bf", size = 2278184, upload-time = "2025-09-29T21:11:37.434Z" }, + { url = "https://files.pythonhosted.org/packages/e3/f7/a10b101b7a6f8836a5adb47f2791f2075d044a6ca123f35985c42edc82d8/fonttools-4.60.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:7b0c6d57ab00dae9529f3faf187f2254ea0aa1e04215cf2f1a8ec277c96661bc", size = 2832953, upload-time = "2025-09-29T21:11:39.616Z" }, + { url = "https://files.pythonhosted.org/packages/ed/fe/7bd094b59c926acf2304d2151354ddbeb74b94812f3dc943c231db09cb41/fonttools-4.60.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:839565cbf14645952d933853e8ade66a463684ed6ed6c9345d0faf1f0e868877", size = 2352706, upload-time = "2025-09-29T21:11:41.826Z" }, + { url = "https://files.pythonhosted.org/packages/c0/ca/4bb48a26ed95a1e7eba175535fe5805887682140ee0a0d10a88e1de84208/fonttools-4.60.1-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8177ec9676ea6e1793c8a084a90b65a9f778771998eb919d05db6d4b1c0b114c", size = 4923716, upload-time = "2025-09-29T21:11:43.893Z" }, + { url = "https://files.pythonhosted.org/packages/b8/9f/2cb82999f686c1d1ddf06f6ae1a9117a880adbec113611cc9d22b2fdd465/fonttools-4.60.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:996a4d1834524adbb423385d5a629b868ef9d774670856c63c9a0408a3063401", size = 4968175, upload-time = "2025-09-29T21:11:46.439Z" }, + { url = "https://files.pythonhosted.org/packages/18/79/be569699e37d166b78e6218f2cde8c550204f2505038cdd83b42edc469b9/fonttools-4.60.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a46b2f450bc79e06ef3b6394f0c68660529ed51692606ad7f953fc2e448bc903", size = 4911031, upload-time = "2025-09-29T21:11:48.977Z" }, + { url = "https://files.pythonhosted.org/packages/cc/9f/89411cc116effaec5260ad519162f64f9c150e5522a27cbb05eb62d0c05b/fonttools-4.60.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6ec722ee589e89a89f5b7574f5c45604030aa6ae24cb2c751e2707193b466fed", size = 5062966, upload-time = "2025-09-29T21:11:54.344Z" }, + { url = "https://files.pythonhosted.org/packages/62/a1/f888221934b5731d46cb9991c7a71f30cb1f97c0ef5fcf37f8da8fce6c8e/fonttools-4.60.1-cp312-cp312-win32.whl", hash = "sha256:b2cf105cee600d2de04ca3cfa1f74f1127f8455b71dbad02b9da6ec266e116d6", size = 2218750, upload-time = "2025-09-29T21:11:56.601Z" }, + { url = "https://files.pythonhosted.org/packages/88/8f/a55b5550cd33cd1028601df41acd057d4be20efa5c958f417b0c0613924d/fonttools-4.60.1-cp312-cp312-win_amd64.whl", hash = "sha256:992775c9fbe2cf794786fa0ffca7f09f564ba3499b8fe9f2f80bd7197db60383", size = 2267026, upload-time = "2025-09-29T21:11:58.852Z" }, + { url = "https://files.pythonhosted.org/packages/c7/93/0dd45cd283c32dea1545151d8c3637b4b8c53cdb3a625aeb2885b184d74d/fonttools-4.60.1-py3-none-any.whl", hash = "sha256:906306ac7afe2156fcf0042173d6ebbb05416af70f6b370967b47f8f00103bbb", size = 1143175, upload-time = "2025-09-29T21:13:24.134Z" }, +] + +[[package]] +name = "frozenlist" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875, upload-time = "2025-10-06T05:38:17.865Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/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/bc/03/077f869d540370db12165c0aa51640a873fb661d8b315d1d4d67b284d7ac/frozenlist-1.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:09474e9831bc2b2199fad6da3c14c7b0fbdd377cce9d3d77131be28906cb7d84", size = 86912, upload-time = "2025-10-06T05:35:45.98Z" }, + { url = "https://files.pythonhosted.org/packages/df/b5/7610b6bd13e4ae77b96ba85abea1c8cb249683217ef09ac9e0ae93f25a91/frozenlist-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:17c883ab0ab67200b5f964d2b9ed6b00971917d5d8a92df149dc2c9779208ee9", size = 50046, upload-time = "2025-10-06T05:35:47.009Z" }, + { url = "https://files.pythonhosted.org/packages/6e/ef/0e8f1fe32f8a53dd26bdd1f9347efe0778b0fddf62789ea683f4cc7d787d/frozenlist-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fa47e444b8ba08fffd1c18e8cdb9a75db1b6a27f17507522834ad13ed5922b93", size = 50119, upload-time = "2025-10-06T05:35:48.38Z" }, + { url = "https://files.pythonhosted.org/packages/11/b1/71a477adc7c36e5fb628245dfbdea2166feae310757dea848d02bd0689fd/frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2552f44204b744fba866e573be4c1f9048d6a324dfe14475103fd51613eb1d1f", size = 231067, upload-time = "2025-10-06T05:35:49.97Z" }, + { url = "https://files.pythonhosted.org/packages/45/7e/afe40eca3a2dc19b9904c0f5d7edfe82b5304cb831391edec0ac04af94c2/frozenlist-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:957e7c38f250991e48a9a73e6423db1bb9dd14e722a10f6b8bb8e16a0f55f695", size = 233160, upload-time = "2025-10-06T05:35:51.729Z" }, + { url = "https://files.pythonhosted.org/packages/a6/aa/7416eac95603ce428679d273255ffc7c998d4132cfae200103f164b108aa/frozenlist-1.8.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8585e3bb2cdea02fc88ffa245069c36555557ad3609e83be0ec71f54fd4abb52", size = 228544, upload-time = "2025-10-06T05:35:53.246Z" }, + { url = "https://files.pythonhosted.org/packages/8b/3d/2a2d1f683d55ac7e3875e4263d28410063e738384d3adc294f5ff3d7105e/frozenlist-1.8.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:edee74874ce20a373d62dc28b0b18b93f645633c2943fd90ee9d898550770581", size = 243797, upload-time = "2025-10-06T05:35:54.497Z" }, + { url = "https://files.pythonhosted.org/packages/78/1e/2d5565b589e580c296d3bb54da08d206e797d941a83a6fdea42af23be79c/frozenlist-1.8.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c9a63152fe95756b85f31186bddf42e4c02c6321207fd6601a1c89ebac4fe567", size = 247923, upload-time = "2025-10-06T05:35:55.861Z" }, + { url = "https://files.pythonhosted.org/packages/aa/c3/65872fcf1d326a7f101ad4d86285c403c87be7d832b7470b77f6d2ed5ddc/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b6db2185db9be0a04fecf2f241c70b63b1a242e2805be291855078f2b404dd6b", size = 230886, upload-time = "2025-10-06T05:35:57.399Z" }, + { url = "https://files.pythonhosted.org/packages/a0/76/ac9ced601d62f6956f03cc794f9e04c81719509f85255abf96e2510f4265/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f4be2e3d8bc8aabd566f8d5b8ba7ecc09249d74ba3c9ed52e54dc23a293f0b92", size = 245731, upload-time = "2025-10-06T05:35:58.563Z" }, + { url = "https://files.pythonhosted.org/packages/b9/49/ecccb5f2598daf0b4a1415497eba4c33c1e8ce07495eb07d2860c731b8d5/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c8d1634419f39ea6f5c427ea2f90ca85126b54b50837f31497f3bf38266e853d", size = 241544, upload-time = "2025-10-06T05:35:59.719Z" }, + { url = "https://files.pythonhosted.org/packages/53/4b/ddf24113323c0bbcc54cb38c8b8916f1da7165e07b8e24a717b4a12cbf10/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1a7fa382a4a223773ed64242dbe1c9c326ec09457e6b8428efb4118c685c3dfd", size = 241806, upload-time = "2025-10-06T05:36:00.959Z" }, + { url = "https://files.pythonhosted.org/packages/a7/fb/9b9a084d73c67175484ba2789a59f8eebebd0827d186a8102005ce41e1ba/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:11847b53d722050808926e785df837353bd4d75f1d494377e59b23594d834967", size = 229382, upload-time = "2025-10-06T05:36:02.22Z" }, + { url = "https://files.pythonhosted.org/packages/95/a3/c8fb25aac55bf5e12dae5c5aa6a98f85d436c1dc658f21c3ac73f9fa95e5/frozenlist-1.8.0-cp311-cp311-win32.whl", hash = "sha256:27c6e8077956cf73eadd514be8fb04d77fc946a7fe9f7fe167648b0b9085cc25", size = 39647, upload-time = "2025-10-06T05:36:03.409Z" }, + { url = "https://files.pythonhosted.org/packages/0a/f5/603d0d6a02cfd4c8f2a095a54672b3cf967ad688a60fb9faf04fc4887f65/frozenlist-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac913f8403b36a2c8610bbfd25b8013488533e71e62b4b4adce9c86c8cea905b", size = 44064, upload-time = "2025-10-06T05:36:04.368Z" }, + { url = "https://files.pythonhosted.org/packages/5d/16/c2c9ab44e181f043a86f9a8f84d5124b62dbcb3a02c0977ec72b9ac1d3e0/frozenlist-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:d4d3214a0f8394edfa3e303136d0575eece0745ff2b47bd2cb2e66dd92d4351a", size = 39937, upload-time = "2025-10-06T05:36:05.669Z" }, + { url = "https://files.pythonhosted.org/packages/69/29/948b9aa87e75820a38650af445d2ef2b6b8a6fab1a23b6bb9e4ef0be2d59/frozenlist-1.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1", size = 87782, upload-time = "2025-10-06T05:36:06.649Z" }, + { url = "https://files.pythonhosted.org/packages/64/80/4f6e318ee2a7c0750ed724fa33a4bdf1eacdc5a39a7a24e818a773cd91af/frozenlist-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b", size = 50594, upload-time = "2025-10-06T05:36:07.69Z" }, + { url = "https://files.pythonhosted.org/packages/2b/94/5c8a2b50a496b11dd519f4a24cb5496cf125681dd99e94c604ccdea9419a/frozenlist-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4", size = 50448, upload-time = "2025-10-06T05:36:08.78Z" }, + { url = "https://files.pythonhosted.org/packages/6a/bd/d91c5e39f490a49df14320f4e8c80161cfcce09f1e2cde1edd16a551abb3/frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383", size = 242411, upload-time = "2025-10-06T05:36:09.801Z" }, + { url = "https://files.pythonhosted.org/packages/8f/83/f61505a05109ef3293dfb1ff594d13d64a2324ac3482be2cedc2be818256/frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4", size = 243014, upload-time = "2025-10-06T05:36:11.394Z" }, + { url = "https://files.pythonhosted.org/packages/d8/cb/cb6c7b0f7d4023ddda30cf56b8b17494eb3a79e3fda666bf735f63118b35/frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8", size = 234909, upload-time = "2025-10-06T05:36:12.598Z" }, + { url = "https://files.pythonhosted.org/packages/31/c5/cd7a1f3b8b34af009fb17d4123c5a778b44ae2804e3ad6b86204255f9ec5/frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b", size = 250049, upload-time = "2025-10-06T05:36:14.065Z" }, + { url = "https://files.pythonhosted.org/packages/c0/01/2f95d3b416c584a1e7f0e1d6d31998c4a795f7544069ee2e0962a4b60740/frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52", size = 256485, upload-time = "2025-10-06T05:36:15.39Z" }, + { url = "https://files.pythonhosted.org/packages/ce/03/024bf7720b3abaebcff6d0793d73c154237b85bdf67b7ed55e5e9596dc9a/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29", size = 237619, upload-time = "2025-10-06T05:36:16.558Z" }, + { url = "https://files.pythonhosted.org/packages/69/fa/f8abdfe7d76b731f5d8bd217827cf6764d4f1d9763407e42717b4bed50a0/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3", size = 250320, upload-time = "2025-10-06T05:36:17.821Z" }, + { url = "https://files.pythonhosted.org/packages/f5/3c/b051329f718b463b22613e269ad72138cc256c540f78a6de89452803a47d/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143", size = 246820, upload-time = "2025-10-06T05:36:19.046Z" }, + { url = "https://files.pythonhosted.org/packages/0f/ae/58282e8f98e444b3f4dd42448ff36fa38bef29e40d40f330b22e7108f565/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608", size = 250518, upload-time = "2025-10-06T05:36:20.763Z" }, + { url = "https://files.pythonhosted.org/packages/8f/96/007e5944694d66123183845a106547a15944fbbb7154788cbf7272789536/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa", size = 239096, upload-time = "2025-10-06T05:36:22.129Z" }, + { url = "https://files.pythonhosted.org/packages/66/bb/852b9d6db2fa40be96f29c0d1205c306288f0684df8fd26ca1951d461a56/frozenlist-1.8.0-cp312-cp312-win32.whl", hash = "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf", size = 39985, upload-time = "2025-10-06T05:36:23.661Z" }, + { url = "https://files.pythonhosted.org/packages/b8/af/38e51a553dd66eb064cdf193841f16f077585d4d28394c2fa6235cb41765/frozenlist-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746", size = 44591, upload-time = "2025-10-06T05:36:24.958Z" }, + { url = "https://files.pythonhosted.org/packages/a7/06/1dc65480ab147339fecc70797e9c2f69d9cea9cf38934ce08df070fdb9cb/frozenlist-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd", size = 40102, upload-time = "2025-10-06T05:36:26.333Z" }, + { url = "https://files.pythonhosted.org/packages/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://download.pytorch.org/whl/cu126" } +sdist = { url = "https://files.pythonhosted.org/packages/24/7f/2747c0d332b9acfa75dc84447a066fdf812b5a6b8d30472b74d309bfe8cb/fsspec-2025.10.0.tar.gz", hash = "sha256:b6789427626f068f9a83ca4e8a3cc050850b6c0f71f99ddb4f542b8266a26a59" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/02/a6b21098b1d5d6249b7c5ab69dde30108a71e4e819d4a9778f1de1d5b70d/fsspec-2025.10.0-py3-none-any.whl", hash = "sha256:7c7712353ae7d875407f97715f0e1ffcc21e33d5b24556cb1e090ae9409ec61d" }, +] + +[package.optional-dependencies] +http = [ + { name = "aiohttp" }, +] + +[[package]] +name = "gdown" +version = "5.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "beautifulsoup4" }, + { name = "filelock" }, + { 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" } +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 = "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.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, upload-time = "2025-07-24T03:45:54.871Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/01/61/d4b89fec821f72385526e1b9d9a3a0385dda4a72b206d28049e2c7cd39b8/gitpython-3.1.45-py3-none-any.whl", hash = "sha256:8908cb2e02fb3b93b7eb0f2827125cb699869470432cc885f019b8fd0fccff77", size = 208168, upload-time = "2025-07-24T03:45:52.517Z" }, +] + +[[package]] +name = "grad-cam" +version = "1.5.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "matplotlib" }, + { name = "numpy" }, + { name = "opencv-python" }, + { name = "pillow" }, + { name = "scikit-learn" }, + { name = "torch" }, + { name = "torchvision" }, + { name = "tqdm" }, + { name = "ttach" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ee/b3/e8b060e69d4de4b4d8a86868762dbc1ecaa58affa538a8af201a38a408ef/grad-cam-1.5.5.tar.gz", hash = "sha256:690c433d226d35c89c9eb170462db204909cb06b39c7381e6880a49b6fc37015", size = 7783293, upload-time = "2025-04-07T05:13:54.984Z" } + +[[package]] +name = "graphviz" +version = "0.21" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f8/b3/3ac91e9be6b761a4b30d66ff165e54439dcd48b83f4e20d644867215f6ca/graphviz-0.21.tar.gz", hash = "sha256:20743e7183be82aaaa8ad6c93f8893c923bd6658a04c32ee115edb3c8a835f78", size = 200434, upload-time = "2025-06-15T09:35:05.824Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/4c/e0ce1ef95d4000ebc1c11801f9b944fa5910ecc15b5e351865763d8657f8/graphviz-0.21-py3-none-any.whl", hash = "sha256:54f33de9f4f911d7e84e4191749cac8cc5653f815b06738c54db9a15ab8b1e42", size = 47300, upload-time = "2025-06-15T09:35:04.433Z" }, +] + +[[package]] +name = "greenlet" +version = "3.2.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/03/b8/704d753a5a45507a7aab61f18db9509302ed3d0a27ac7e0359ec2905b1a6/greenlet-3.2.4.tar.gz", hash = "sha256:0dca0d95ff849f9a364385f36ab49f50065d76964944638be9691e1832e9f86d", size = 188260, upload-time = "2025-08-07T13:24:33.51Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7d/ed/6bfa4109fcb23a58819600392564fea69cdc6551ffd5e69ccf1d52a40cbc/greenlet-3.2.4-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:8c68325b0d0acf8d91dde4e6f930967dd52a5302cd4062932a6b2e7c2969f47c", size = 271061, upload-time = "2025-08-07T13:17:15.373Z" }, + { url = "https://files.pythonhosted.org/packages/2a/fc/102ec1a2fc015b3a7652abab7acf3541d58c04d3d17a8d3d6a44adae1eb1/greenlet-3.2.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:94385f101946790ae13da500603491f04a76b6e4c059dab271b3ce2e283b2590", size = 629475, upload-time = "2025-08-07T13:42:54.009Z" }, + { url = "https://files.pythonhosted.org/packages/c5/26/80383131d55a4ac0fb08d71660fd77e7660b9db6bdb4e8884f46d9f2cc04/greenlet-3.2.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f10fd42b5ee276335863712fa3da6608e93f70629c631bf77145021600abc23c", size = 640802, upload-time = "2025-08-07T13:45:25.52Z" }, + { url = "https://files.pythonhosted.org/packages/9f/7c/e7833dbcd8f376f3326bd728c845d31dcde4c84268d3921afcae77d90d08/greenlet-3.2.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c8c9e331e58180d0d83c5b7999255721b725913ff6bc6cf39fa2a45841a4fd4b", size = 636703, upload-time = "2025-08-07T13:53:12.622Z" }, + { url = "https://files.pythonhosted.org/packages/e9/49/547b93b7c0428ede7b3f309bc965986874759f7d89e4e04aeddbc9699acb/greenlet-3.2.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:58b97143c9cc7b86fc458f215bd0932f1757ce649e05b640fea2e79b54cedb31", size = 635417, upload-time = "2025-08-07T13:18:25.189Z" }, + { url = "https://files.pythonhosted.org/packages/7f/91/ae2eb6b7979e2f9b035a9f612cf70f1bf54aad4e1d125129bef1eae96f19/greenlet-3.2.4-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c2ca18a03a8cfb5b25bc1cbe20f3d9a4c80d8c3b13ba3df49ac3961af0b1018d", size = 584358, upload-time = "2025-08-07T13:18:23.708Z" }, + { url = "https://files.pythonhosted.org/packages/f7/85/433de0c9c0252b22b16d413c9407e6cb3b41df7389afc366ca204dbc1393/greenlet-3.2.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9fe0a28a7b952a21e2c062cd5756d34354117796c6d9215a87f55e38d15402c5", size = 1113550, upload-time = "2025-08-07T13:42:37.467Z" }, + { url = "https://files.pythonhosted.org/packages/a1/8d/88f3ebd2bc96bf7747093696f4335a0a8a4c5acfcf1b757717c0d2474ba3/greenlet-3.2.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8854167e06950ca75b898b104b63cc646573aa5fef1353d4508ecdd1ee76254f", size = 1137126, upload-time = "2025-08-07T13:18:20.239Z" }, + { url = "https://files.pythonhosted.org/packages/f1/29/74242b7d72385e29bcc5563fba67dad94943d7cd03552bac320d597f29b2/greenlet-3.2.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f47617f698838ba98f4ff4189aef02e7343952df3a615f847bb575c3feb177a7", size = 1544904, upload-time = "2025-11-04T12:42:04.763Z" }, + { url = "https://files.pythonhosted.org/packages/c8/e2/1572b8eeab0f77df5f6729d6ab6b141e4a84ee8eb9bc8c1e7918f94eda6d/greenlet-3.2.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:af41be48a4f60429d5cad9d22175217805098a9ef7c40bfef44f7669fb9d74d8", size = 1611228, upload-time = "2025-11-04T12:42:08.423Z" }, + { url = "https://files.pythonhosted.org/packages/d6/6f/b60b0291d9623c496638c582297ead61f43c4b72eef5e9c926ef4565ec13/greenlet-3.2.4-cp310-cp310-win_amd64.whl", hash = "sha256:73f49b5368b5359d04e18d15828eecc1806033db5233397748f4ca813ff1056c", size = 298654, upload-time = "2025-08-07T13:50:00.469Z" }, + { url = "https://files.pythonhosted.org/packages/a4/de/f28ced0a67749cac23fecb02b694f6473f47686dff6afaa211d186e2ef9c/greenlet-3.2.4-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:96378df1de302bc38e99c3a9aa311967b7dc80ced1dcc6f171e99842987882a2", size = 272305, upload-time = "2025-08-07T13:15:41.288Z" }, + { url = "https://files.pythonhosted.org/packages/09/16/2c3792cba130000bf2a31c5272999113f4764fd9d874fb257ff588ac779a/greenlet-3.2.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1ee8fae0519a337f2329cb78bd7a8e128ec0f881073d43f023c7b8d4831d5246", size = 632472, upload-time = "2025-08-07T13:42:55.044Z" }, + { url = "https://files.pythonhosted.org/packages/ae/8f/95d48d7e3d433e6dae5b1682e4292242a53f22df82e6d3dda81b1701a960/greenlet-3.2.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:94abf90142c2a18151632371140b3dba4dee031633fe614cb592dbb6c9e17bc3", size = 644646, upload-time = "2025-08-07T13:45:26.523Z" }, + { url = "https://files.pythonhosted.org/packages/d5/5e/405965351aef8c76b8ef7ad370e5da58d57ef6068df197548b015464001a/greenlet-3.2.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:4d1378601b85e2e5171b99be8d2dc85f594c79967599328f95c1dc1a40f1c633", size = 640519, upload-time = "2025-08-07T13:53:13.928Z" }, + { url = "https://files.pythonhosted.org/packages/25/5d/382753b52006ce0218297ec1b628e048c4e64b155379331f25a7316eb749/greenlet-3.2.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0db5594dce18db94f7d1650d7489909b57afde4c580806b8d9203b6e79cdc079", size = 639707, upload-time = "2025-08-07T13:18:27.146Z" }, + { url = "https://files.pythonhosted.org/packages/1f/8e/abdd3f14d735b2929290a018ecf133c901be4874b858dd1c604b9319f064/greenlet-3.2.4-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2523e5246274f54fdadbce8494458a2ebdcdbc7b802318466ac5606d3cded1f8", size = 587684, upload-time = "2025-08-07T13:18:25.164Z" }, + { url = "https://files.pythonhosted.org/packages/5d/65/deb2a69c3e5996439b0176f6651e0052542bb6c8f8ec2e3fba97c9768805/greenlet-3.2.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1987de92fec508535687fb807a5cea1560f6196285a4cde35c100b8cd632cc52", size = 1116647, upload-time = "2025-08-07T13:42:38.655Z" }, + { url = "https://files.pythonhosted.org/packages/3f/cc/b07000438a29ac5cfb2194bfc128151d52f333cee74dd7dfe3fb733fc16c/greenlet-3.2.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:55e9c5affaa6775e2c6b67659f3a71684de4c549b3dd9afca3bc773533d284fa", size = 1142073, upload-time = "2025-08-07T13:18:21.737Z" }, + { url = "https://files.pythonhosted.org/packages/67/24/28a5b2fa42d12b3d7e5614145f0bd89714c34c08be6aabe39c14dd52db34/greenlet-3.2.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c9c6de1940a7d828635fbd254d69db79e54619f165ee7ce32fda763a9cb6a58c", size = 1548385, upload-time = "2025-11-04T12:42:11.067Z" }, + { url = "https://files.pythonhosted.org/packages/6a/05/03f2f0bdd0b0ff9a4f7b99333d57b53a7709c27723ec8123056b084e69cd/greenlet-3.2.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:03c5136e7be905045160b1b9fdca93dd6727b180feeafda6818e6496434ed8c5", size = 1613329, upload-time = "2025-11-04T12:42:12.928Z" }, + { url = "https://files.pythonhosted.org/packages/d8/0f/30aef242fcab550b0b3520b8e3561156857c94288f0332a79928c31a52cf/greenlet-3.2.4-cp311-cp311-win_amd64.whl", hash = "sha256:9c40adce87eaa9ddb593ccb0fa6a07caf34015a29bf8d344811665b573138db9", size = 299100, upload-time = "2025-08-07T13:44:12.287Z" }, + { url = "https://files.pythonhosted.org/packages/44/69/9b804adb5fd0671f367781560eb5eb586c4d495277c93bde4307b9e28068/greenlet-3.2.4-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3b67ca49f54cede0186854a008109d6ee71f66bd57bb36abd6d0a0267b540cdd", size = 274079, upload-time = "2025-08-07T13:15:45.033Z" }, + { url = "https://files.pythonhosted.org/packages/46/e9/d2a80c99f19a153eff70bc451ab78615583b8dac0754cfb942223d2c1a0d/greenlet-3.2.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ddf9164e7a5b08e9d22511526865780a576f19ddd00d62f8a665949327fde8bb", size = 640997, upload-time = "2025-08-07T13:42:56.234Z" }, + { url = "https://files.pythonhosted.org/packages/3b/16/035dcfcc48715ccd345f3a93183267167cdd162ad123cd93067d86f27ce4/greenlet-3.2.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f28588772bb5fb869a8eb331374ec06f24a83a9c25bfa1f38b6993afe9c1e968", size = 655185, upload-time = "2025-08-07T13:45:27.624Z" }, + { url = "https://files.pythonhosted.org/packages/31/da/0386695eef69ffae1ad726881571dfe28b41970173947e7c558d9998de0f/greenlet-3.2.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:5c9320971821a7cb77cfab8d956fa8e39cd07ca44b6070db358ceb7f8797c8c9", size = 649926, upload-time = "2025-08-07T13:53:15.251Z" }, + { url = "https://files.pythonhosted.org/packages/68/88/69bf19fd4dc19981928ceacbc5fd4bb6bc2215d53199e367832e98d1d8fe/greenlet-3.2.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c60a6d84229b271d44b70fb6e5fa23781abb5d742af7b808ae3f6efd7c9c60f6", size = 651839, upload-time = "2025-08-07T13:18:30.281Z" }, + { url = "https://files.pythonhosted.org/packages/19/0d/6660d55f7373b2ff8152401a83e02084956da23ae58cddbfb0b330978fe9/greenlet-3.2.4-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b3812d8d0c9579967815af437d96623f45c0f2ae5f04e366de62a12d83a8fb0", size = 607586, upload-time = "2025-08-07T13:18:28.544Z" }, + { url = "https://files.pythonhosted.org/packages/8e/1a/c953fdedd22d81ee4629afbb38d2f9d71e37d23caace44775a3a969147d4/greenlet-3.2.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:abbf57b5a870d30c4675928c37278493044d7c14378350b3aa5d484fa65575f0", size = 1123281, upload-time = "2025-08-07T13:42:39.858Z" }, + { url = "https://files.pythonhosted.org/packages/3f/c7/12381b18e21aef2c6bd3a636da1088b888b97b7a0362fac2e4de92405f97/greenlet-3.2.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:20fb936b4652b6e307b8f347665e2c615540d4b42b3b4c8a321d8286da7e520f", size = 1151142, upload-time = "2025-08-07T13:18:22.981Z" }, + { url = "https://files.pythonhosted.org/packages/27/45/80935968b53cfd3f33cf99ea5f08227f2646e044568c9b1555b58ffd61c2/greenlet-3.2.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ee7a6ec486883397d70eec05059353b8e83eca9168b9f3f9a361971e77e0bcd0", size = 1564846, upload-time = "2025-11-04T12:42:15.191Z" }, + { url = "https://files.pythonhosted.org/packages/69/02/b7c30e5e04752cb4db6202a3858b149c0710e5453b71a3b2aec5d78a1aab/greenlet-3.2.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:326d234cbf337c9c3def0676412eb7040a35a768efc92504b947b3e9cfc7543d", size = 1633814, upload-time = "2025-11-04T12:42:17.175Z" }, + { url = "https://files.pythonhosted.org/packages/e9/08/b0814846b79399e585f974bbeebf5580fbe59e258ea7be64d9dfb253c84f/greenlet-3.2.4-cp312-cp312-win_amd64.whl", hash = "sha256:a7d4e128405eea3814a12cc2605e0e6aedb4035bf32697f72deca74de4105e02", size = 299899, upload-time = "2025-08-07T13:38:53.448Z" }, +] + +[[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" } +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" }, +] + +[[package]] +name = "huggingface-hub" +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/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, upload-time = "2025-10-23T12:11:59.557Z" }, +] + +[[package]] +name = "hydra-core" +version = "1.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "antlr4-python3-runtime" }, + { name = "omegaconf" }, + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6d/8e/07e42bc434a847154083b315779b0a81d567154504624e181caf2c71cd98/hydra-core-1.3.2.tar.gz", hash = "sha256:8a878ed67216997c3e9d88a8e72e7b4767e81af37afb4ea3334b269a4390a824", size = 3263494, upload-time = "2023-02-23T18:33:43.03Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/50/e0edd38dcd63fb26a8547f13d28f7a008bc4a3fd4eb4ff030673f22ad41a/hydra_core-1.3.2-py3-none-any.whl", hash = "sha256:fa0238a9e31df3373b35b0bfb672c34cc92718d21f81311d8996a16de1141d8b", size = 154547, upload-time = "2023-02-23T18:33:40.801Z" }, +] + +[[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" } +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" }, +] + +[[package]] +name = "idna" +version = "3.4" +source = { registry = "https://download.pytorch.org/whl/cu126" } +wheels = [ + { url = "https://download.pytorch.org/whl/idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2" }, +] + +[[package]] +name = "iniconfig" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://download.pytorch.org/whl/cu126" } +dependencies = [ + { name = "markupsafe" }, +] +wheels = [ + { url = "https://download.pytorch.org/whl/jinja2-3.1.6-py3-none-any.whl" }, +] + +[[package]] +name = "joblib" +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, upload-time = "2025-08-27T12:15:46.575Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/e8/685f47e0d754320684db4425a0967f7d3fa70126bffd76110b7009a0090f/joblib-1.5.2-py3-none-any.whl", hash = "sha256:4e1f0bdbb987e6d843c70cf43714cb276623def372df3c22fe5266b2670bc241", size = 308396, upload-time = "2025-08-27T12:15:45.188Z" }, +] + +[[package]] +name = "kaleido" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "choreographer" }, + { name = "logistro" }, + { name = "orjson" }, + { name = "packaging" }, + { name = "pytest-timeout" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/38/ad/76eec859b71eda803a88ea50ed3f270281254656bb23d19eb0a39aa706a0/kaleido-1.2.0.tar.gz", hash = "sha256:fa621a14423e8effa2895a2526be00af0cf21655be1b74b7e382c171d12e71ef", size = 64160, upload-time = "2025-11-04T21:24:23.833Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4b/97/f6de8d4af54d6401d6581a686cce3e3e2371a79ba459a449104e026c08bc/kaleido-1.2.0-py3-none-any.whl", hash = "sha256:c27ed82b51df6b923d0e656feac221343a0dbcd2fb9bc7e6b1db97f61e9a1513", size = 68997, upload-time = "2025-11-04T21:24:21.704Z" }, +] + +[[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/6f/ab/c80b0d5a9d8a1a65f4f815f2afff9798b12c3b9f31f1d304dd233dd920e2/kiwisolver-1.4.9-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:eb14a5da6dc7642b0f3a18f13654847cd8b7a2550e2645a5bda677862b03ba16", size = 124167, upload-time = "2025-08-10T21:25:53.403Z" }, + { url = "https://files.pythonhosted.org/packages/a0/c0/27fe1a68a39cf62472a300e2879ffc13c0538546c359b86f149cc19f6ac3/kiwisolver-1.4.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:39a219e1c81ae3b103643d2aedb90f1ef22650deb266ff12a19e7773f3e5f089", size = 66579, upload-time = "2025-08-10T21:25:54.79Z" }, + { url = "https://files.pythonhosted.org/packages/31/a2/a12a503ac1fd4943c50f9822678e8015a790a13b5490354c68afb8489814/kiwisolver-1.4.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2405a7d98604b87f3fc28b1716783534b1b4b8510d8142adca34ee0bc3c87543", size = 65309, upload-time = "2025-08-10T21:25:55.76Z" }, + { url = "https://files.pythonhosted.org/packages/66/e1/e533435c0be77c3f64040d68d7a657771194a63c279f55573188161e81ca/kiwisolver-1.4.9-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:dc1ae486f9abcef254b5618dfb4113dd49f94c68e3e027d03cf0143f3f772b61", size = 1435596, upload-time = "2025-08-10T21:25:56.861Z" }, + { url = "https://files.pythonhosted.org/packages/67/1e/51b73c7347f9aabdc7215aa79e8b15299097dc2f8e67dee2b095faca9cb0/kiwisolver-1.4.9-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a1f570ce4d62d718dce3f179ee78dac3b545ac16c0c04bb363b7607a949c0d1", size = 1246548, upload-time = "2025-08-10T21:25:58.246Z" }, + { url = "https://files.pythonhosted.org/packages/21/aa/72a1c5d1e430294f2d32adb9542719cfb441b5da368d09d268c7757af46c/kiwisolver-1.4.9-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb27e7b78d716c591e88e0a09a2139c6577865d7f2e152488c2cc6257f460872", size = 1263618, upload-time = "2025-08-10T21:25:59.857Z" }, + { url = "https://files.pythonhosted.org/packages/a3/af/db1509a9e79dbf4c260ce0cfa3903ea8945f6240e9e59d1e4deb731b1a40/kiwisolver-1.4.9-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:15163165efc2f627eb9687ea5f3a28137217d217ac4024893d753f46bce9de26", size = 1317437, upload-time = "2025-08-10T21:26:01.105Z" }, + { url = "https://files.pythonhosted.org/packages/e0/f2/3ea5ee5d52abacdd12013a94130436e19969fa183faa1e7c7fbc89e9a42f/kiwisolver-1.4.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bdee92c56a71d2b24c33a7d4c2856bd6419d017e08caa7802d2963870e315028", size = 2195742, upload-time = "2025-08-10T21:26:02.675Z" }, + { url = "https://files.pythonhosted.org/packages/6f/9b/1efdd3013c2d9a2566aa6a337e9923a00590c516add9a1e89a768a3eb2fc/kiwisolver-1.4.9-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:412f287c55a6f54b0650bd9b6dce5aceddb95864a1a90c87af16979d37c89771", size = 2290810, upload-time = "2025-08-10T21:26:04.009Z" }, + { url = "https://files.pythonhosted.org/packages/fb/e5/cfdc36109ae4e67361f9bc5b41323648cb24a01b9ade18784657e022e65f/kiwisolver-1.4.9-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2c93f00dcba2eea70af2be5f11a830a742fe6b579a1d4e00f47760ef13be247a", size = 2461579, upload-time = "2025-08-10T21:26:05.317Z" }, + { url = "https://files.pythonhosted.org/packages/62/86/b589e5e86c7610842213994cdea5add00960076bef4ae290c5fa68589cac/kiwisolver-1.4.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f117e1a089d9411663a3207ba874f31be9ac8eaa5b533787024dc07aeb74f464", size = 2268071, upload-time = "2025-08-10T21:26:06.686Z" }, + { url = "https://files.pythonhosted.org/packages/3b/c6/f8df8509fd1eee6c622febe54384a96cfaf4d43bf2ccec7a0cc17e4715c9/kiwisolver-1.4.9-cp311-cp311-win_amd64.whl", hash = "sha256:be6a04e6c79819c9a8c2373317d19a96048e5a3f90bec587787e86a1153883c2", size = 73840, upload-time = "2025-08-10T21:26:07.94Z" }, + { url = "https://files.pythonhosted.org/packages/e2/2d/16e0581daafd147bc11ac53f032a2b45eabac897f42a338d0a13c1e5c436/kiwisolver-1.4.9-cp311-cp311-win_arm64.whl", hash = "sha256:0ae37737256ba2de764ddc12aed4956460277f00c4996d51a197e72f62f5eec7", size = 65159, upload-time = "2025-08-10T21:26:09.048Z" }, + { url = "https://files.pythonhosted.org/packages/86/c9/13573a747838aeb1c76e3267620daa054f4152444d1f3d1a2324b78255b5/kiwisolver-1.4.9-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ac5a486ac389dddcc5bef4f365b6ae3ffff2c433324fb38dd35e3fab7c957999", size = 123686, upload-time = "2025-08-10T21:26:10.034Z" }, + { url = "https://files.pythonhosted.org/packages/51/ea/2ecf727927f103ffd1739271ca19c424d0e65ea473fbaeea1c014aea93f6/kiwisolver-1.4.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f2ba92255faa7309d06fe44c3a4a97efe1c8d640c2a79a5ef728b685762a6fd2", size = 66460, upload-time = "2025-08-10T21:26:11.083Z" }, + { url = "https://files.pythonhosted.org/packages/5b/5a/51f5464373ce2aeb5194508298a508b6f21d3867f499556263c64c621914/kiwisolver-1.4.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a2899935e724dd1074cb568ce7ac0dce28b2cd6ab539c8e001a8578eb106d14", size = 64952, upload-time = "2025-08-10T21:26:12.058Z" }, + { url = "https://files.pythonhosted.org/packages/70/90/6d240beb0f24b74371762873e9b7f499f1e02166a2d9c5801f4dbf8fa12e/kiwisolver-1.4.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f6008a4919fdbc0b0097089f67a1eb55d950ed7e90ce2cc3e640abadd2757a04", size = 1474756, upload-time = "2025-08-10T21:26:13.096Z" }, + { url = "https://files.pythonhosted.org/packages/12/42/f36816eaf465220f683fb711efdd1bbf7a7005a2473d0e4ed421389bd26c/kiwisolver-1.4.9-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:67bb8b474b4181770f926f7b7d2f8c0248cbcb78b660fdd41a47054b28d2a752", size = 1276404, upload-time = "2025-08-10T21:26:14.457Z" }, + { url = "https://files.pythonhosted.org/packages/2e/64/bc2de94800adc830c476dce44e9b40fd0809cddeef1fde9fcf0f73da301f/kiwisolver-1.4.9-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2327a4a30d3ee07d2fbe2e7933e8a37c591663b96ce42a00bc67461a87d7df77", size = 1294410, upload-time = "2025-08-10T21:26:15.73Z" }, + { url = "https://files.pythonhosted.org/packages/5f/42/2dc82330a70aa8e55b6d395b11018045e58d0bb00834502bf11509f79091/kiwisolver-1.4.9-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7a08b491ec91b1d5053ac177afe5290adacf1f0f6307d771ccac5de30592d198", size = 1343631, upload-time = "2025-08-10T21:26:17.045Z" }, + { url = "https://files.pythonhosted.org/packages/22/fd/f4c67a6ed1aab149ec5a8a401c323cee7a1cbe364381bb6c9c0d564e0e20/kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d8fc5c867c22b828001b6a38d2eaeb88160bf5783c6cb4a5e440efc981ce286d", size = 2224963, upload-time = "2025-08-10T21:26:18.737Z" }, + { url = "https://files.pythonhosted.org/packages/45/aa/76720bd4cb3713314677d9ec94dcc21ced3f1baf4830adde5bb9b2430a5f/kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:3b3115b2581ea35bb6d1f24a4c90af37e5d9b49dcff267eeed14c3893c5b86ab", size = 2321295, upload-time = "2025-08-10T21:26:20.11Z" }, + { url = "https://files.pythonhosted.org/packages/80/19/d3ec0d9ab711242f56ae0dc2fc5d70e298bb4a1f9dfab44c027668c673a1/kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:858e4c22fb075920b96a291928cb7dea5644e94c0ee4fcd5af7e865655e4ccf2", size = 2487987, upload-time = "2025-08-10T21:26:21.49Z" }, + { url = "https://files.pythonhosted.org/packages/39/e9/61e4813b2c97e86b6fdbd4dd824bf72d28bcd8d4849b8084a357bc0dd64d/kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ed0fecd28cc62c54b262e3736f8bb2512d8dcfdc2bcf08be5f47f96bf405b145", size = 2291817, upload-time = "2025-08-10T21:26:22.812Z" }, + { url = "https://files.pythonhosted.org/packages/a0/41/85d82b0291db7504da3c2defe35c9a8a5c9803a730f297bd823d11d5fb77/kiwisolver-1.4.9-cp312-cp312-win_amd64.whl", hash = "sha256:f68208a520c3d86ea51acf688a3e3002615a7f0238002cccc17affecc86a8a54", size = 73895, upload-time = "2025-08-10T21:26:24.37Z" }, + { url = "https://files.pythonhosted.org/packages/e2/92/5f3068cf15ee5cb624a0c7596e67e2a0bb2adee33f71c379054a491d07da/kiwisolver-1.4.9-cp312-cp312-win_arm64.whl", hash = "sha256:2c1a4f57df73965f3f14df20b80ee29e6a7930a57d2d9e8491a25f676e197c60", size = 64992, upload-time = "2025-08-10T21:26:25.732Z" }, + { 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" }, + { url = "https://files.pythonhosted.org/packages/a3/0f/36d89194b5a32c054ce93e586d4049b6c2c22887b0eb229c61c68afd3078/kiwisolver-1.4.9-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:720e05574713db64c356e86732c0f3c5252818d05f9df320f0ad8380641acea5", size = 60104, upload-time = "2025-08-10T21:27:43.287Z" }, + { url = "https://files.pythonhosted.org/packages/52/ba/4ed75f59e4658fd21fe7dde1fee0ac397c678ec3befba3fe6482d987af87/kiwisolver-1.4.9-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:17680d737d5335b552994a2008fab4c851bcd7de33094a82067ef3a576ff02fa", size = 58592, upload-time = "2025-08-10T21:27:44.314Z" }, + { url = "https://files.pythonhosted.org/packages/33/01/a8ea7c5ea32a9b45ceeaee051a04c8ed4320f5add3c51bfa20879b765b70/kiwisolver-1.4.9-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:85b5352f94e490c028926ea567fc569c52ec79ce131dadb968d3853e809518c2", size = 80281, upload-time = "2025-08-10T21:27:45.369Z" }, + { url = "https://files.pythonhosted.org/packages/da/e3/dbd2ecdce306f1d07a1aaf324817ee993aab7aee9db47ceac757deabafbe/kiwisolver-1.4.9-pp311-pypy311_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:464415881e4801295659462c49461a24fb107c140de781d55518c4b80cb6790f", size = 78009, upload-time = "2025-08-10T21:27:46.376Z" }, + { url = "https://files.pythonhosted.org/packages/da/e9/0d4add7873a73e462aeb45c036a2dead2562b825aa46ba326727b3f31016/kiwisolver-1.4.9-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:fb940820c63a9590d31d88b815e7a3aa5915cad3ce735ab45f0c730b39547de1", size = 73929, upload-time = "2025-08-10T21:27:48.236Z" }, +] + +[[package]] +name = "lightgbm" +version = "4.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scipy", version = "1.16.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/68/0b/a2e9f5c5da7ef047cc60cef37f86185088845e8433e54d2e7ed439cce8a3/lightgbm-4.6.0.tar.gz", hash = "sha256:cb1c59720eb569389c0ba74d14f52351b573af489f230032a1c9f314f8bab7fe", size = 1703705, upload-time = "2025-02-15T04:03:03.111Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f2/75/cffc9962cca296bc5536896b7e65b4a7cdeb8db208e71b9c0133c08f8f7e/lightgbm-4.6.0-py3-none-macosx_10_15_x86_64.whl", hash = "sha256:b7a393de8a334d5c8e490df91270f0763f83f959574d504c7ccb9eee4aef70ed", size = 2010151, upload-time = "2025-02-15T04:02:50.961Z" }, + { url = "https://files.pythonhosted.org/packages/21/1b/550ee378512b78847930f5d74228ca1fdba2a7fbdeaac9aeccc085b0e257/lightgbm-4.6.0-py3-none-macosx_12_0_arm64.whl", hash = "sha256:2dafd98d4e02b844ceb0b61450a660681076b1ea6c7adb8c566dfd66832aafad", size = 1592172, upload-time = "2025-02-15T04:02:53.937Z" }, + { url = "https://files.pythonhosted.org/packages/64/41/4fbde2c3d29e25ee7c41d87df2f2e5eda65b431ee154d4d462c31041846c/lightgbm-4.6.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:4d68712bbd2b57a0b14390cbf9376c1d5ed773fa2e71e099cac588703b590336", size = 3454567, upload-time = "2025-02-15T04:02:56.443Z" }, + { url = "https://files.pythonhosted.org/packages/42/86/dabda8fbcb1b00bcfb0003c3776e8ade1aa7b413dff0a2c08f457dace22f/lightgbm-4.6.0-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:cb19b5afea55b5b61cbb2131095f50538bd608a00655f23ad5d25ae3e3bf1c8d", size = 3569831, upload-time = "2025-02-15T04:02:58.925Z" }, + { url = "https://files.pythonhosted.org/packages/5e/23/f8b28ca248bb629b9e08f877dd2965d1994e1674a03d67cd10c5246da248/lightgbm-4.6.0-py3-none-win_amd64.whl", hash = "sha256:37089ee95664b6550a7189d887dbf098e3eadab03537e411f52c63c121e3ba4b", size = 1451509, upload-time = "2025-02-15T04:03:01.515Z" }, +] + +[[package]] +name = "lightning" +version = "2.5.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "fsspec", extra = ["http"] }, + { name = "lightning-utilities" }, + { name = "packaging" }, + { name = "pytorch-lightning" }, + { name = "pyyaml" }, + { name = "torch" }, + { name = "torchmetrics" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e9/da/289e17b2d4631b885771ce10ab7fe19c6c0ab2b1208d1dda418818ffbbfd/lightning-2.5.6.tar.gz", hash = "sha256:57b6abe87080895bc237fb7f36b7b4abaa2793760cbca00e3907e56607e0ed27", size = 640106, upload-time = "2025-11-05T20:53:06.823Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/23/dc/d7804f13928b6a81a0e948cfecbf0071e8cc74e3f341c704e23e75e504ad/lightning-2.5.6-py3-none-any.whl", hash = "sha256:25bb2053078c2efc57c082fda89dfbd975dfa76beb08def191947c2b571a8c8a", size = 827915, upload-time = "2025-11-05T20:53:03.169Z" }, +] + +[[package]] +name = "lightning-utilities" +version = "0.11.8" +source = { registry = "https://download.pytorch.org/whl/cu126" } +dependencies = [ + { name = "packaging" }, + { name = "setuptools" }, + { name = "typing-extensions" }, +] +wheels = [ + { url = "https://download.pytorch.org/whl/lightning_utilities-0.11.8-py3-none-any.whl", hash = "sha256:a57edb34a44258f0c61eed8b8b88926766e9052f5e60bbe69e4871a2b2bfd970" }, +] + +[[package]] +name = "llvmlite" +version = "0.45.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/99/8d/5baf1cef7f9c084fb35a8afbde88074f0d6a727bc63ef764fe0e7543ba40/llvmlite-0.45.1.tar.gz", hash = "sha256:09430bb9d0bb58fc45a45a57c7eae912850bedc095cd0810a57de109c69e1c32", size = 185600, upload-time = "2025-10-01T17:59:52.046Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cf/6d/585c84ddd9d2a539a3c3487792b3cf3f988e28ec4fa281bf8b0e055e1166/llvmlite-0.45.1-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:1b1af0c910af0978aa55fa4f60bbb3e9f39b41e97c2a6d94d199897be62ba07a", size = 43043523, upload-time = "2025-10-01T18:02:58.621Z" }, + { url = "https://files.pythonhosted.org/packages/ae/34/992bd12d3ff245e0801bcf6013961daa8c19c9b9c2e61cb4b8bce94566f9/llvmlite-0.45.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02a164db2d79088bbd6e0d9633b4fe4021d6379d7e4ac7cc85ed5f44b06a30c5", size = 37253122, upload-time = "2025-10-01T18:03:55.159Z" }, + { url = "https://files.pythonhosted.org/packages/a6/7b/6d7585998a5991fa74dc925aae57913ba8c7c2efff909de9d34cc1cd3c27/llvmlite-0.45.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f2d47f34e4029e6df3395de34cc1c66440a8d72712993a6e6168db228686711b", size = 56288210, upload-time = "2025-10-01T18:00:41.978Z" }, + { url = "https://files.pythonhosted.org/packages/b5/e2/a4abea058633bfc82eb08fd69ce242c118fdb9b0abad1fdcbe0bc6aedab5/llvmlite-0.45.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f7319e5f9f90720578a7f56fbc805bdfb4bc071b507c7611f170d631c3c0f1e0", size = 55140958, upload-time = "2025-10-01T18:01:55.694Z" }, + { url = "https://files.pythonhosted.org/packages/74/c0/233468e96ed287b953239c3b24b1d69df47c6ba9262bfdca98eda7e83a04/llvmlite-0.45.1-cp310-cp310-win_amd64.whl", hash = "sha256:4edb62e685867799e336723cb9787ec6598d51d0b1ed9af0f38e692aa757e898", size = 38132232, upload-time = "2025-10-01T18:04:41.538Z" }, + { url = "https://files.pythonhosted.org/packages/04/ad/9bdc87b2eb34642c1cfe6bcb4f5db64c21f91f26b010f263e7467e7536a3/llvmlite-0.45.1-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:60f92868d5d3af30b4239b50e1717cb4e4e54f6ac1c361a27903b318d0f07f42", size = 43043526, upload-time = "2025-10-01T18:03:15.051Z" }, + { url = "https://files.pythonhosted.org/packages/a5/ea/c25c6382f452a943b4082da5e8c1665ce29a62884e2ec80608533e8e82d5/llvmlite-0.45.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:98baab513e19beb210f1ef39066288784839a44cd504e24fff5d17f1b3cf0860", size = 37253118, upload-time = "2025-10-01T18:04:06.783Z" }, + { url = "https://files.pythonhosted.org/packages/fe/af/85fc237de98b181dbbe8647324331238d6c52a3554327ccdc83ced28efba/llvmlite-0.45.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3adc2355694d6a6fbcc024d59bb756677e7de506037c878022d7b877e7613a36", size = 56288209, upload-time = "2025-10-01T18:01:00.168Z" }, + { url = "https://files.pythonhosted.org/packages/0a/df/3daf95302ff49beff4230065e3178cd40e71294968e8d55baf4a9e560814/llvmlite-0.45.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2f3377a6db40f563058c9515dedcc8a3e562d8693a106a28f2ddccf2c8fcf6ca", size = 55140958, upload-time = "2025-10-01T18:02:11.199Z" }, + { url = "https://files.pythonhosted.org/packages/a4/56/4c0d503fe03bac820ecdeb14590cf9a248e120f483bcd5c009f2534f23f0/llvmlite-0.45.1-cp311-cp311-win_amd64.whl", hash = "sha256:f9c272682d91e0d57f2a76c6d9ebdfccc603a01828cdbe3d15273bdca0c3363a", size = 38132232, upload-time = "2025-10-01T18:04:52.181Z" }, + { url = "https://files.pythonhosted.org/packages/e2/7c/82cbd5c656e8991bcc110c69d05913be2229302a92acb96109e166ae31fb/llvmlite-0.45.1-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:28e763aba92fe9c72296911e040231d486447c01d4f90027c8e893d89d49b20e", size = 43043524, upload-time = "2025-10-01T18:03:30.666Z" }, + { url = "https://files.pythonhosted.org/packages/9d/bc/5314005bb2c7ee9f33102c6456c18cc81745d7055155d1218f1624463774/llvmlite-0.45.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1a53f4b74ee9fd30cb3d27d904dadece67a7575198bd80e687ee76474620735f", size = 37253123, upload-time = "2025-10-01T18:04:18.177Z" }, + { url = "https://files.pythonhosted.org/packages/96/76/0f7154952f037cb320b83e1c952ec4a19d5d689cf7d27cb8a26887d7bbc1/llvmlite-0.45.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b3796b1b1e1c14dcae34285d2f4ea488402fbd2c400ccf7137603ca3800864f", size = 56288211, upload-time = "2025-10-01T18:01:24.079Z" }, + { url = "https://files.pythonhosted.org/packages/00/b1/0b581942be2683ceb6862d558979e87387e14ad65a1e4db0e7dd671fa315/llvmlite-0.45.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:779e2f2ceefef0f4368548685f0b4adde34e5f4b457e90391f570a10b348d433", size = 55140958, upload-time = "2025-10-01T18:02:30.482Z" }, + { url = "https://files.pythonhosted.org/packages/33/94/9ba4ebcf4d541a325fd8098ddc073b663af75cc8b065b6059848f7d4dce7/llvmlite-0.45.1-cp312-cp312-win_amd64.whl", hash = "sha256:9e6c9949baf25d9aa9cd7cf0f6d011b9ca660dd17f5ba2b23bdbdb77cc86b116", size = 38132231, upload-time = "2025-10-01T18:05:03.664Z" }, +] + +[[package]] +name = "logistro" +version = "2.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/08/90/bfd7a6fab22bdfafe48ed3c4831713cb77b4779d18ade5e248d5dbc0ca22/logistro-2.0.1.tar.gz", hash = "sha256:8446affc82bab2577eb02bfcbcae196ae03129287557287b6a070f70c1985047", size = 8398, upload-time = "2025-11-01T02:41:18.81Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/6aa79ba3570bddd1bf7e951c6123f806751e58e8cce736bad77b2cf348d7/logistro-2.0.1-py3-none-any.whl", hash = "sha256:06ffa127b9fb4ac8b1972ae6b2a9d7fde57598bf5939cd708f43ec5bba2d31eb", size = 8555, upload-time = "2025-11-01T02:41:17.587Z" }, +] + +[[package]] +name = "loguru" +version = "0.7.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "win32-setctime", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3a/05/a1dae3dffd1116099471c643b8924f5aa6524411dc6c63fdae648c4f1aca/loguru-0.7.3.tar.gz", hash = "sha256:19480589e77d47b8d85b2c827ad95d49bf31b0dcde16593892eb51dd18706eb6", size = 63559, upload-time = "2024-12-06T11:20:56.608Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/29/0348de65b8cc732daa3e33e67806420b2ae89bdce2b04af740289c5c6c8c/loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c", size = 61595, upload-time = "2024-12-06T11:20:54.538Z" }, +] + +[[package]] +name = "mako" +version = "1.3.10" +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" } +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" }, +] + +[[package]] +name = "markdown-it-py" +version = "4.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" }, +] + +[[package]] +name = "markupsafe" +version = "3.0.2" +source = { registry = "https://download.pytorch.org/whl/cu126" } +wheels = [ + { url = "https://download.pytorch.org/whl/MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl" }, + { url = "https://download.pytorch.org/whl/MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl" }, + { url = "https://download.pytorch.org/whl/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" }, + { url = "https://download.pytorch.org/whl/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" }, + { url = "https://download.pytorch.org/whl/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl" }, + { url = "https://download.pytorch.org/whl/MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl" }, + { url = "https://download.pytorch.org/whl/MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl" }, + { url = "https://download.pytorch.org/whl/MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl" }, + { url = "https://download.pytorch.org/whl/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" }, + { url = "https://download.pytorch.org/whl/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" }, + { url = "https://download.pytorch.org/whl/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl" }, + { url = "https://download.pytorch.org/whl/MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl" }, + { url = "https://download.pytorch.org/whl/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl" }, + { url = "https://download.pytorch.org/whl/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl" }, + { url = "https://download.pytorch.org/whl/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" }, + { url = "https://download.pytorch.org/whl/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" }, + { url = "https://download.pytorch.org/whl/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl" }, + { url = "https://download.pytorch.org/whl/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl" }, +] + +[[package]] +name = "matplotlib" +version = "3.10.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "contourpy", version = "1.3.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "contourpy", version = "1.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "cycler" }, + { name = "fonttools" }, + { name = "kiwisolver" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pillow" }, + { 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, upload-time = "2025-10-09T00:28:00.669Z" } +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, upload-time = "2025-10-09T00:26:06.023Z" }, + { 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, upload-time = "2025-10-09T00:26:08.669Z" }, + { 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, upload-time = "2025-10-09T00:26:10.607Z" }, + { 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, upload-time = "2025-10-09T00:26:12.594Z" }, + { 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, upload-time = "2025-10-09T00:26:14.758Z" }, + { url = "https://files.pythonhosted.org/packages/65/7d/954b3067120456f472cce8fdcacaf4a5fcd522478db0c37bb243c7cb59dd/matplotlib-3.10.7-cp310-cp310-win_amd64.whl", hash = "sha256:b498e9e4022f93de2d5a37615200ca01297ceebbb56fe4c833f46862a490f9e3", size = 8108174, upload-time = "2025-10-09T00:26:17.015Z" }, + { url = "https://files.pythonhosted.org/packages/fc/bc/0fb489005669127ec13f51be0c6adc074d7cf191075dab1da9fe3b7a3cfc/matplotlib-3.10.7-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:53b492410a6cd66c7a471de6c924f6ede976e963c0f3097a3b7abfadddc67d0a", size = 8257507, upload-time = "2025-10-09T00:26:19.073Z" }, + { url = "https://files.pythonhosted.org/packages/e2/6a/d42588ad895279ff6708924645b5d2ed54a7fb2dc045c8a804e955aeace1/matplotlib-3.10.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d9749313deb729f08207718d29c86246beb2ea3fdba753595b55901dee5d2fd6", size = 8119565, upload-time = "2025-10-09T00:26:21.023Z" }, + { url = "https://files.pythonhosted.org/packages/10/b7/4aa196155b4d846bd749cf82aa5a4c300cf55a8b5e0dfa5b722a63c0f8a0/matplotlib-3.10.7-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2222c7ba2cbde7fe63032769f6eb7e83ab3227f47d997a8453377709b7fe3a5a", size = 8692668, upload-time = "2025-10-09T00:26:22.967Z" }, + { url = "https://files.pythonhosted.org/packages/e6/e7/664d2b97016f46683a02d854d730cfcf54ff92c1dafa424beebef50f831d/matplotlib-3.10.7-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e91f61a064c92c307c5a9dc8c05dc9f8a68f0a3be199d9a002a0622e13f874a1", size = 9521051, upload-time = "2025-10-09T00:26:25.041Z" }, + { url = "https://files.pythonhosted.org/packages/a8/a3/37aef1404efa615f49b5758a5e0261c16dd88f389bc1861e722620e4a754/matplotlib-3.10.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6f1851eab59ca082c95df5a500106bad73672645625e04538b3ad0f69471ffcc", size = 9576878, upload-time = "2025-10-09T00:26:27.478Z" }, + { url = "https://files.pythonhosted.org/packages/33/cd/b145f9797126f3f809d177ca378de57c45413c5099c5990de2658760594a/matplotlib-3.10.7-cp311-cp311-win_amd64.whl", hash = "sha256:6516ce375109c60ceec579e699524e9d504cd7578506f01150f7a6bc174a775e", size = 8115142, upload-time = "2025-10-09T00:26:29.774Z" }, + { url = "https://files.pythonhosted.org/packages/2e/39/63bca9d2b78455ed497fcf51a9c71df200a11048f48249038f06447fa947/matplotlib-3.10.7-cp311-cp311-win_arm64.whl", hash = "sha256:b172db79759f5f9bc13ef1c3ef8b9ee7b37b0247f987fbbbdaa15e4f87fd46a9", size = 7992439, upload-time = "2025-10-09T00:26:40.32Z" }, + { url = "https://files.pythonhosted.org/packages/be/b3/09eb0f7796932826ec20c25b517d568627754f6c6462fca19e12c02f2e12/matplotlib-3.10.7-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7a0edb7209e21840e8361e91ea84ea676658aa93edd5f8762793dec77a4a6748", size = 8272389, upload-time = "2025-10-09T00:26:42.474Z" }, + { url = "https://files.pythonhosted.org/packages/11/0b/1ae80ddafb8652fd8046cb5c8460ecc8d4afccb89e2c6d6bec61e04e1eaf/matplotlib-3.10.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c380371d3c23e0eadf8ebff114445b9f970aff2010198d498d4ab4c3b41eea4f", size = 8128247, upload-time = "2025-10-09T00:26:44.77Z" }, + { url = "https://files.pythonhosted.org/packages/7d/18/95ae2e242d4a5c98bd6e90e36e128d71cf1c7e39b0874feaed3ef782e789/matplotlib-3.10.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d5f256d49fea31f40f166a5e3131235a5d2f4b7f44520b1cf0baf1ce568ccff0", size = 8696996, upload-time = "2025-10-09T00:26:46.792Z" }, + { url = "https://files.pythonhosted.org/packages/7e/3d/5b559efc800bd05cb2033aa85f7e13af51958136a48327f7c261801ff90a/matplotlib-3.10.7-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:11ae579ac83cdf3fb72573bb89f70e0534de05266728740d478f0f818983c695", size = 9530153, upload-time = "2025-10-09T00:26:49.07Z" }, + { url = "https://files.pythonhosted.org/packages/88/57/eab4a719fd110312d3c220595d63a3c85ec2a39723f0f4e7fa7e6e3f74ba/matplotlib-3.10.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4c14b6acd16cddc3569a2d515cfdd81c7a68ac5639b76548cfc1a9e48b20eb65", size = 9593093, upload-time = "2025-10-09T00:26:51.067Z" }, + { url = "https://files.pythonhosted.org/packages/31/3c/80816f027b3a4a28cd2a0a6ef7f89a2db22310e945cd886ec25bfb399221/matplotlib-3.10.7-cp312-cp312-win_amd64.whl", hash = "sha256:0d8c32b7ea6fb80b1aeff5a2ceb3fb9778e2759e899d9beff75584714afcc5ee", size = 8122771, upload-time = "2025-10-09T00:26:53.296Z" }, + { url = "https://files.pythonhosted.org/packages/de/77/ef1fc78bfe99999b2675435cc52120887191c566b25017d78beaabef7f2d/matplotlib-3.10.7-cp312-cp312-win_arm64.whl", hash = "sha256:5f3f6d315dcc176ba7ca6e74c7768fb7e4cf566c49cb143f6bc257b62e634ed8", size = 7992812, upload-time = "2025-10-09T00:26:54.882Z" }, + { 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, upload-time = "2025-10-09T00:27:48.806Z" }, + { 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, upload-time = "2025-10-09T00:27:50.876Z" }, + { 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, upload-time = "2025-10-09T00:27:52.597Z" }, + { url = "https://files.pythonhosted.org/packages/58/8f/76d5dc21ac64a49e5498d7f0472c0781dae442dd266a67458baec38288ec/matplotlib-3.10.7-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:15112bcbaef211bd663fa935ec33313b948e214454d949b723998a43357b17b0", size = 8252283, upload-time = "2025-10-09T00:27:54.739Z" }, + { url = "https://files.pythonhosted.org/packages/27/0d/9c5d4c2317feb31d819e38c9f947c942f42ebd4eb935fc6fd3518a11eaa7/matplotlib-3.10.7-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d2a959c640cdeecdd2ec3136e8ea0441da59bcaf58d67e9c590740addba2cb68", size = 8116733, upload-time = "2025-10-09T00:27:56.406Z" }, + { url = "https://files.pythonhosted.org/packages/9a/cc/3fe688ff1355010937713164caacf9ed443675ac48a997bab6ed23b3f7c0/matplotlib-3.10.7-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3886e47f64611046bc1db523a09dd0a0a6bed6081e6f90e13806dd1d1d1b5e91", size = 8693919, upload-time = "2025-10-09T00:27:58.41Z" }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, +] + +[[package]] +name = "mpmath" +version = "1.3.0" +source = { registry = "https://download.pytorch.org/whl/cu126" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c" }, +] + +[[package]] +name = "multidict" +version = "6.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +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/34/9e/5c727587644d67b2ed479041e4b1c58e30afc011e3d45d25bbe35781217c/multidict-6.7.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4d409aa42a94c0b3fa617708ef5276dfe81012ba6753a0370fcc9d0195d0a1fc", size = 76604, upload-time = "2025-10-06T14:48:54.277Z" }, + { url = "https://files.pythonhosted.org/packages/17/e4/67b5c27bd17c085a5ea8f1ec05b8a3e5cba0ca734bfcad5560fb129e70ca/multidict-6.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:14c9e076eede3b54c636f8ce1c9c252b5f057c62131211f0ceeec273810c9721", size = 44715, upload-time = "2025-10-06T14:48:55.445Z" }, + { url = "https://files.pythonhosted.org/packages/4d/e1/866a5d77be6ea435711bef2a4291eed11032679b6b28b56b4776ab06ba3e/multidict-6.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4c09703000a9d0fa3c3404b27041e574cc7f4df4c6563873246d0e11812a94b6", size = 44332, upload-time = "2025-10-06T14:48:56.706Z" }, + { url = "https://files.pythonhosted.org/packages/31/61/0c2d50241ada71ff61a79518db85ada85fdabfcf395d5968dae1cbda04e5/multidict-6.7.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a265acbb7bb33a3a2d626afbe756371dce0279e7b17f4f4eda406459c2b5ff1c", size = 245212, upload-time = "2025-10-06T14:48:58.042Z" }, + { url = "https://files.pythonhosted.org/packages/ac/e0/919666a4e4b57fff1b57f279be1c9316e6cdc5de8a8b525d76f6598fefc7/multidict-6.7.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:51cb455de290ae462593e5b1cb1118c5c22ea7f0d3620d9940bf695cea5a4bd7", size = 246671, upload-time = "2025-10-06T14:49:00.004Z" }, + { url = "https://files.pythonhosted.org/packages/a1/cc/d027d9c5a520f3321b65adea289b965e7bcbd2c34402663f482648c716ce/multidict-6.7.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:db99677b4457c7a5c5a949353e125ba72d62b35f74e26da141530fbb012218a7", size = 225491, upload-time = "2025-10-06T14:49:01.393Z" }, + { url = "https://files.pythonhosted.org/packages/75/c4/bbd633980ce6155a28ff04e6a6492dd3335858394d7bb752d8b108708558/multidict-6.7.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f470f68adc395e0183b92a2f4689264d1ea4b40504a24d9882c27375e6662bb9", size = 257322, upload-time = "2025-10-06T14:49:02.745Z" }, + { url = "https://files.pythonhosted.org/packages/4c/6d/d622322d344f1f053eae47e033b0b3f965af01212de21b10bcf91be991fb/multidict-6.7.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0db4956f82723cc1c270de9c6e799b4c341d327762ec78ef82bb962f79cc07d8", size = 254694, upload-time = "2025-10-06T14:49:04.15Z" }, + { url = "https://files.pythonhosted.org/packages/a8/9f/78f8761c2705d4c6d7516faed63c0ebdac569f6db1bef95e0d5218fdc146/multidict-6.7.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3e56d780c238f9e1ae66a22d2adf8d16f485381878250db8d496623cd38b22bd", size = 246715, upload-time = "2025-10-06T14:49:05.967Z" }, + { url = "https://files.pythonhosted.org/packages/78/59/950818e04f91b9c2b95aab3d923d9eabd01689d0dcd889563988e9ea0fd8/multidict-6.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9d14baca2ee12c1a64740d4531356ba50b82543017f3ad6de0deb943c5979abb", size = 243189, upload-time = "2025-10-06T14:49:07.37Z" }, + { url = "https://files.pythonhosted.org/packages/7a/3d/77c79e1934cad2ee74991840f8a0110966d9599b3af95964c0cd79bb905b/multidict-6.7.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:295a92a76188917c7f99cda95858c822f9e4aae5824246bba9b6b44004ddd0a6", size = 237845, upload-time = "2025-10-06T14:49:08.759Z" }, + { url = "https://files.pythonhosted.org/packages/63/1b/834ce32a0a97a3b70f86437f685f880136677ac00d8bce0027e9fd9c2db7/multidict-6.7.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:39f1719f57adbb767ef592a50ae5ebb794220d1188f9ca93de471336401c34d2", size = 246374, upload-time = "2025-10-06T14:49:10.574Z" }, + { url = "https://files.pythonhosted.org/packages/23/ef/43d1c3ba205b5dec93dc97f3fba179dfa47910fc73aaaea4f7ceb41cec2a/multidict-6.7.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:0a13fb8e748dfc94749f622de065dd5c1def7e0d2216dba72b1d8069a389c6ff", size = 253345, upload-time = "2025-10-06T14:49:12.331Z" }, + { url = "https://files.pythonhosted.org/packages/6b/03/eaf95bcc2d19ead522001f6a650ef32811aa9e3624ff0ad37c445c7a588c/multidict-6.7.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e3aa16de190d29a0ea1b48253c57d99a68492c8dd8948638073ab9e74dc9410b", size = 246940, upload-time = "2025-10-06T14:49:13.821Z" }, + { url = "https://files.pythonhosted.org/packages/e8/df/ec8a5fd66ea6cd6f525b1fcbb23511b033c3e9bc42b81384834ffa484a62/multidict-6.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a048ce45dcdaaf1defb76b2e684f997fb5abf74437b6cb7b22ddad934a964e34", size = 242229, upload-time = "2025-10-06T14:49:15.603Z" }, + { url = "https://files.pythonhosted.org/packages/8a/a2/59b405d59fd39ec86d1142630e9049243015a5f5291ba49cadf3c090c541/multidict-6.7.0-cp311-cp311-win32.whl", hash = "sha256:a90af66facec4cebe4181b9e62a68be65e45ac9b52b67de9eec118701856e7ff", size = 41308, upload-time = "2025-10-06T14:49:16.871Z" }, + { url = "https://files.pythonhosted.org/packages/32/0f/13228f26f8b882c34da36efa776c3b7348455ec383bab4a66390e42963ae/multidict-6.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:95b5ffa4349df2887518bb839409bcf22caa72d82beec453216802f475b23c81", size = 46037, upload-time = "2025-10-06T14:49:18.457Z" }, + { url = "https://files.pythonhosted.org/packages/84/1f/68588e31b000535a3207fd3c909ebeec4fb36b52c442107499c18a896a2a/multidict-6.7.0-cp311-cp311-win_arm64.whl", hash = "sha256:329aa225b085b6f004a4955271a7ba9f1087e39dcb7e65f6284a988264a63912", size = 43023, upload-time = "2025-10-06T14:49:19.648Z" }, + { url = "https://files.pythonhosted.org/packages/c2/9e/9f61ac18d9c8b475889f32ccfa91c9f59363480613fc807b6e3023d6f60b/multidict-6.7.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8a3862568a36d26e650a19bb5cbbba14b71789032aebc0423f8cc5f150730184", size = 76877, upload-time = "2025-10-06T14:49:20.884Z" }, + { url = "https://files.pythonhosted.org/packages/38/6f/614f09a04e6184f8824268fce4bc925e9849edfa654ddd59f0b64508c595/multidict-6.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:960c60b5849b9b4f9dcc9bea6e3626143c252c74113df2c1540aebce70209b45", size = 45467, upload-time = "2025-10-06T14:49:22.054Z" }, + { url = "https://files.pythonhosted.org/packages/b3/93/c4f67a436dd026f2e780c433277fff72be79152894d9fc36f44569cab1a6/multidict-6.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2049be98fb57a31b4ccf870bf377af2504d4ae35646a19037ec271e4c07998aa", size = 43834, upload-time = "2025-10-06T14:49:23.566Z" }, + { url = "https://files.pythonhosted.org/packages/7f/f5/013798161ca665e4a422afbc5e2d9e4070142a9ff8905e482139cd09e4d0/multidict-6.7.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0934f3843a1860dd465d38895c17fce1f1cb37295149ab05cd1b9a03afacb2a7", size = 250545, upload-time = "2025-10-06T14:49:24.882Z" }, + { url = "https://files.pythonhosted.org/packages/71/2f/91dbac13e0ba94669ea5119ba267c9a832f0cb65419aca75549fcf09a3dc/multidict-6.7.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b3e34f3a1b8131ba06f1a73adab24f30934d148afcd5f5de9a73565a4404384e", size = 258305, upload-time = "2025-10-06T14:49:26.778Z" }, + { url = "https://files.pythonhosted.org/packages/ef/b0/754038b26f6e04488b48ac621f779c341338d78503fb45403755af2df477/multidict-6.7.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:efbb54e98446892590dc2458c19c10344ee9a883a79b5cec4bc34d6656e8d546", size = 242363, upload-time = "2025-10-06T14:49:28.562Z" }, + { url = "https://files.pythonhosted.org/packages/87/15/9da40b9336a7c9fa606c4cf2ed80a649dffeb42b905d4f63a1d7eb17d746/multidict-6.7.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a35c5fc61d4f51eb045061e7967cfe3123d622cd500e8868e7c0c592a09fedc4", size = 268375, upload-time = "2025-10-06T14:49:29.96Z" }, + { url = "https://files.pythonhosted.org/packages/82/72/c53fcade0cc94dfaad583105fd92b3a783af2091eddcb41a6d5a52474000/multidict-6.7.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29fe6740ebccba4175af1b9b87bf553e9c15cd5868ee967e010efcf94e4fd0f1", size = 269346, upload-time = "2025-10-06T14:49:31.404Z" }, + { url = "https://files.pythonhosted.org/packages/0d/e2/9baffdae21a76f77ef8447f1a05a96ec4bc0a24dae08767abc0a2fe680b8/multidict-6.7.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:123e2a72e20537add2f33a79e605f6191fba2afda4cbb876e35c1a7074298a7d", size = 256107, upload-time = "2025-10-06T14:49:32.974Z" }, + { url = "https://files.pythonhosted.org/packages/3c/06/3f06f611087dc60d65ef775f1fb5aca7c6d61c6db4990e7cda0cef9b1651/multidict-6.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b284e319754366c1aee2267a2036248b24eeb17ecd5dc16022095e747f2f4304", size = 253592, upload-time = "2025-10-06T14:49:34.52Z" }, + { url = "https://files.pythonhosted.org/packages/20/24/54e804ec7945b6023b340c412ce9c3f81e91b3bf5fa5ce65558740141bee/multidict-6.7.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:803d685de7be4303b5a657b76e2f6d1240e7e0a8aa2968ad5811fa2285553a12", size = 251024, upload-time = "2025-10-06T14:49:35.956Z" }, + { url = "https://files.pythonhosted.org/packages/14/48/011cba467ea0b17ceb938315d219391d3e421dfd35928e5dbdc3f4ae76ef/multidict-6.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c04a328260dfd5db8c39538f999f02779012268f54614902d0afc775d44e0a62", size = 251484, upload-time = "2025-10-06T14:49:37.631Z" }, + { url = "https://files.pythonhosted.org/packages/0d/2f/919258b43bb35b99fa127435cfb2d91798eb3a943396631ef43e3720dcf4/multidict-6.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8a19cdb57cd3df4cd865849d93ee14920fb97224300c88501f16ecfa2604b4e0", size = 263579, upload-time = "2025-10-06T14:49:39.502Z" }, + { url = "https://files.pythonhosted.org/packages/31/22/a0e884d86b5242b5a74cf08e876bdf299e413016b66e55511f7a804a366e/multidict-6.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9b2fd74c52accced7e75de26023b7dccee62511a600e62311b918ec5c168fc2a", size = 259654, upload-time = "2025-10-06T14:49:41.32Z" }, + { url = "https://files.pythonhosted.org/packages/b2/e5/17e10e1b5c5f5a40f2fcbb45953c9b215f8a4098003915e46a93f5fcaa8f/multidict-6.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3e8bfdd0e487acf992407a140d2589fe598238eaeffa3da8448d63a63cd363f8", size = 251511, upload-time = "2025-10-06T14:49:46.021Z" }, + { url = "https://files.pythonhosted.org/packages/e3/9a/201bb1e17e7af53139597069c375e7b0dcbd47594604f65c2d5359508566/multidict-6.7.0-cp312-cp312-win32.whl", hash = "sha256:dd32a49400a2c3d52088e120ee00c1e3576cbff7e10b98467962c74fdb762ed4", size = 41895, upload-time = "2025-10-06T14:49:48.718Z" }, + { url = "https://files.pythonhosted.org/packages/46/e2/348cd32faad84eaf1d20cce80e2bb0ef8d312c55bca1f7fa9865e7770aaf/multidict-6.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:92abb658ef2d7ef22ac9f8bb88e8b6c3e571671534e029359b6d9e845923eb1b", size = 46073, upload-time = "2025-10-06T14:49:50.28Z" }, + { url = "https://files.pythonhosted.org/packages/25/ec/aad2613c1910dce907480e0c3aa306905830f25df2e54ccc9dea450cb5aa/multidict-6.7.0-cp312-cp312-win_arm64.whl", hash = "sha256:490dab541a6a642ce1a9d61a4781656b346a55c13038f0b1244653828e3a83ec", size = 43226, upload-time = "2025-10-06T14:49:52.304Z" }, + { 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]] +name = "murmurhash" +version = "1.0.15" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/23/2e/88c147931ea9725d634840d538622e94122bceaf346233349b7b5c62964b/murmurhash-1.0.15.tar.gz", hash = "sha256:58e2b27b7847f9e2a6edf10b47a8c8dd70a4705f45dccb7bf76aeadacf56ba01", size = 13291, upload-time = "2025-11-14T09:51:15.272Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/09/3c/5e59e29fe971365d27f191a5cbf8a5fb492746e458604fe5d39810da4668/murmurhash-1.0.15-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f4989c16053a9a83b02c520dd00a31f0877d5fd2ab8a9b6b75ed9eba0e25c489", size = 27463, upload-time = "2025-11-14T09:49:53.158Z" }, + { url = "https://files.pythonhosted.org/packages/38/3d/ace00a9b82beaa99a8a7a52e98171cfbf13c0066d2f820e84a5d572e3bd0/murmurhash-1.0.15-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:899068ba3d7c371e7edd093852c634cce802fefd9aaddfcc0d2fda1d7433c7f9", size = 27714, upload-time = "2025-11-14T09:49:54.855Z" }, + { url = "https://files.pythonhosted.org/packages/10/0f/34f1c4f97424ea1bc72b1e3bdf61ac34f4c5555ec9163721f1e4cafe5b1d/murmurhash-1.0.15-cp310-cp310-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fe883982114de576c793fd1cf55945c8ee6453ad4c4785ac1a48f84e74fdc650", size = 122570, upload-time = "2025-11-14T09:49:55.977Z" }, + { url = "https://files.pythonhosted.org/packages/b9/75/0019717a16ce5a7b088fc50a3ecb513035e4196c5e569bf4a2e16bcc0414/murmurhash-1.0.15-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:342277d8d7f712d136507fb3ccdba26c076a34ca0f8d1b96f65f0daa556da2e9", size = 123194, upload-time = "2025-11-14T09:49:57.462Z" }, + { url = "https://files.pythonhosted.org/packages/7b/a4/c1c95ce60b816c2255098164e424752779269c93f5d6dceaa213346789a2/murmurhash-1.0.15-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bc54facccb32fe1e97d6231edd4f3e2937467c35658b26aa35bbd6a87ebb7cb0", size = 122461, upload-time = "2025-11-14T09:49:58.686Z" }, + { url = "https://files.pythonhosted.org/packages/63/28/e1f79369a6e8d1a5901346ed2fd3a5c56e647d0b849044870c071cb64e1c/murmurhash-1.0.15-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e525bbd8e26e6b9ab1b56758a59b16c2fffd73bad2f7b8bf361c16f70ff1d980", size = 121676, upload-time = "2025-11-14T09:49:59.888Z" }, + { url = "https://files.pythonhosted.org/packages/1d/7c/e2be1f5387e5898f6551cf81c4220975858b9dbda4d471b133750945599a/murmurhash-1.0.15-cp310-cp310-win_amd64.whl", hash = "sha256:2224f30f7729717644745a6f513ea7662517dfe7b1867cf1588177f64c61df3c", size = 25156, upload-time = "2025-11-14T09:50:01.016Z" }, + { url = "https://files.pythonhosted.org/packages/74/07/0df6e1a753de68368662cbbb8f88558e2c877d3886ac12b30953fb8ed335/murmurhash-1.0.15-cp310-cp310-win_arm64.whl", hash = "sha256:8a181494b5f03ba831f9a13f2de3aab9ef591e508e57239043d65c5c592f5837", size = 23270, upload-time = "2025-11-14T09:50:01.99Z" }, + { url = "https://files.pythonhosted.org/packages/6b/ca/77d3e69924a8eb4508bb4f0ad34e46adbeedeb93616a71080e61e53dad71/murmurhash-1.0.15-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f32307fb9347680bb4fe1cbef6362fb39bd994f1b59abd8c09ca174e44199081", size = 27397, upload-time = "2025-11-14T09:50:03.077Z" }, + { url = "https://files.pythonhosted.org/packages/e6/53/a936f577d35b245d47b310f29e5e9f09fcac776c8c992f1ab51a9fb0cee2/murmurhash-1.0.15-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:539d8405885d1d19c005f3a2313b47e8e54b0ee89915eb8dfbb430b194328e6c", size = 27692, upload-time = "2025-11-14T09:50:04.144Z" }, + { url = "https://files.pythonhosted.org/packages/4d/64/5f8cfd1fd9cbeb43fcff96672f5bd9e7e1598d1c970f808ecd915490dc20/murmurhash-1.0.15-cp311-cp311-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c4cd739a00f5a4602201b74568ddabae46ec304719d9be752fd8f534a9464b5e", size = 128396, upload-time = "2025-11-14T09:50:05.268Z" }, + { url = "https://files.pythonhosted.org/packages/ac/10/d9ce29d559a75db0d8a3f13ea12c7f541ec9de2afca38dc70418b890eedb/murmurhash-1.0.15-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:44d211bcc3ec203c47dac06f48ee871093fcbdffa6652a6cc5ea7180306680a8", size = 128687, upload-time = "2025-11-14T09:50:06.527Z" }, + { url = "https://files.pythonhosted.org/packages/48/cd/dc97ab7e68cdfa1537a56e36dbc846c5a66701cc39ecee2d4399fe61996c/murmurhash-1.0.15-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f9bf47101354fb1dc4b2e313192566f04ba295c28a37e2f71c692759acc1ba3c", size = 128198, upload-time = "2025-11-14T09:50:08.062Z" }, + { url = "https://files.pythonhosted.org/packages/53/73/32f2aaa22c1e4afae337106baf0c938abf36a6cc879cfee83a00461bbbf7/murmurhash-1.0.15-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3c69b4d3bcd6233782a78907fe10b9b7a796bdc5d28060cf097d067bec280a5d", size = 127214, upload-time = "2025-11-14T09:50:09.265Z" }, + { url = "https://files.pythonhosted.org/packages/82/ed/812103a7f353eba2d83655b08205e13a38c93b4db0692f94756e1eb44516/murmurhash-1.0.15-cp311-cp311-win_amd64.whl", hash = "sha256:e43a69496342ce530bdd670264cb7c8f45490b296e4764c837ce577e3c7ebd53", size = 25241, upload-time = "2025-11-14T09:50:10.373Z" }, + { url = "https://files.pythonhosted.org/packages/eb/5f/2c511bdd28f7c24da37a00116ffd0432b65669d098f0d0260c66ac0ffdc2/murmurhash-1.0.15-cp311-cp311-win_arm64.whl", hash = "sha256:f3e99a6ee36ef5372df5f138e3d9c801420776d3641a34a49e5c2555f44edba7", size = 23216, upload-time = "2025-11-14T09:50:11.651Z" }, + { url = "https://files.pythonhosted.org/packages/b6/46/be8522d3456fdccf1b8b049c6d82e7a3c1114c4fc2cfe14b04cba4b3e701/murmurhash-1.0.15-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d37e3ae44746bca80b1a917c2ea625cf216913564ed43f69d2888e5df97db0cb", size = 27884, upload-time = "2025-11-14T09:50:13.133Z" }, + { url = "https://files.pythonhosted.org/packages/ed/cc/630449bf4f6178d7daf948ce46ad00b25d279065fc30abd8d706be3d87e0/murmurhash-1.0.15-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0861cb11039409eaf46878456b7d985ef17b6b484103a6fc367b2ecec846891d", size = 27855, upload-time = "2025-11-14T09:50:14.859Z" }, + { url = "https://files.pythonhosted.org/packages/ff/30/ea8f601a9bf44db99468696efd59eb9cff1157cd55cb586d67116697583f/murmurhash-1.0.15-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5a301decfaccfec70fe55cb01dde2a012c3014a874542eaa7cc73477bb749616", size = 134088, upload-time = "2025-11-14T09:50:15.958Z" }, + { url = "https://files.pythonhosted.org/packages/c9/de/c40ce8c0877d406691e735b8d6e9c815f36a82b499d358313db5dbe219d7/murmurhash-1.0.15-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:32c6fde7bd7e9407003370a07b5f4addacabe1556ad3dc2cac246b7a2bba3400", size = 133978, upload-time = "2025-11-14T09:50:17.572Z" }, + { url = "https://files.pythonhosted.org/packages/47/84/bd49963ecd84ebab2fe66595e2d1ed41d5e8b5153af5dc930f0bd827007c/murmurhash-1.0.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5d8b43a7011540dc3c7ce66f2134df9732e2bc3bbb4a35f6458bc755e48bde26", size = 132956, upload-time = "2025-11-14T09:50:18.742Z" }, + { url = "https://files.pythonhosted.org/packages/4f/7c/2530769c545074417c862583f05f4245644599f1e9ff619b3dfe2969aafc/murmurhash-1.0.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:43bf4541892ecd95963fcd307bf1c575fc0fee1682f41c93007adee71ca2bb40", size = 134184, upload-time = "2025-11-14T09:50:19.941Z" }, + { url = "https://files.pythonhosted.org/packages/84/a4/b249b042f5afe34d14ada2dc4afc777e883c15863296756179652e081c44/murmurhash-1.0.15-cp312-cp312-win_amd64.whl", hash = "sha256:f4ac15a2089dc42e6eb0966622d42d2521590a12c92480aafecf34c085302cca", size = 25647, upload-time = "2025-11-14T09:50:21.049Z" }, + { url = "https://files.pythonhosted.org/packages/13/bf/028179259aebc18fd4ba5cae2601d1d47517427a537ab44336446431a215/murmurhash-1.0.15-cp312-cp312-win_arm64.whl", hash = "sha256:4a70ca4ae19e600d9be3da64d00710e79dde388a4d162f22078d64844d0ebdda", size = 23338, upload-time = "2025-11-14T09:50:22.359Z" }, +] + +[[package]] +name = "narwhals" +version = "2.12.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/93/f8/e1c28f24b641871c14ccae7ba6381f3c7827789a06e947ce975ae8a9075a/narwhals-2.12.0.tar.gz", hash = "sha256:075b6d56f3a222613793e025744b129439ecdff9292ea6615dd983af7ba6ea44", size = 590404, upload-time = "2025-11-17T10:53:28.381Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/9a/c6f79de7ba3a0a8473129936b7b90aa461d3d46fec6f1627672b1dccf4e9/narwhals-2.12.0-py3-none-any.whl", hash = "sha256:baeba5d448a30b04c299a696bd9ee5ff73e4742143e06c49ca316b46539a7cbb", size = 425014, upload-time = "2025-11-17T10:53:26.65Z" }, +] + +[[package]] +name = "networkx" +version = "3.4.2" +source = { registry = "https://download.pytorch.org/whl/cu126" } +resolution-markers = [ + "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'linux'", + "python_full_version < '3.11' and sys_platform == 'darwin'", + "python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux'", +] +sdist = { url = "https://files.pythonhosted.org/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f" }, +] + +[[package]] +name = "networkx" +version = "3.6" +source = { registry = "https://download.pytorch.org/whl/cu126" } +resolution-markers = [ + "python_full_version >= '3.12' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "python_full_version >= '3.12' and platform_machine != 'aarch64' and sys_platform == 'linux'", + "python_full_version >= '3.12' and sys_platform == 'darwin'", + "python_full_version >= '3.12' and sys_platform != 'darwin' and sys_platform != 'linux'", + "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "python_full_version == '3.11.*' and platform_machine != 'aarch64' and sys_platform == 'linux'", + "python_full_version == '3.11.*' and sys_platform == 'darwin'", + "python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux'", +] +sdist = { url = "https://files.pythonhosted.org/packages/e8/fc/7b6fd4d22c8c4dc5704430140d8b3f520531d4fe7328b8f8d03f5a7950e8/networkx-3.6.tar.gz", hash = "sha256:285276002ad1f7f7da0f7b42f004bcba70d381e936559166363707fdad3d72ad" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/c7/d64168da60332c17d24c0d2f08bdf3987e8d1ae9d84b5bbd0eec2eb26a55/networkx-3.6-py3-none-any.whl", hash = "sha256:cdb395b105806062473d3be36458d8f1459a4e4b98e236a66c3a48996e07684f" }, +] + +[[package]] +name = "nltk" +version = "3.9.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "joblib" }, + { name = "regex" }, + { name = "tqdm" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f9/76/3a5e4312c19a028770f86fd7c058cf9f4ec4321c6cf7526bab998a5b683c/nltk-3.9.2.tar.gz", hash = "sha256:0f409e9b069ca4177c1903c3e843eef90c7e92992fa4931ae607da6de49e1419", size = 2887629, upload-time = "2025-10-01T07:19:23.764Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/90/81ac364ef94209c100e12579629dc92bf7a709a84af32f8c551b02c07e94/nltk-3.9.2-py3-none-any.whl", hash = "sha256:1e209d2b3009110635ed9709a67a1a3e33a10f799490fa71cf4bec218c11c88a", size = 1513404, upload-time = "2025-10-01T07:19:21.648Z" }, +] + +[[package]] +name = "nodeenv" +version = "1.9.1" +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, upload-time = "2024-06-04T18:44:11.171Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314, upload-time = "2024-06-04T18:44:08.352Z" }, +] + +[[package]] +name = "numba" +version = "0.62.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "llvmlite" }, + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a3/20/33dbdbfe60e5fd8e3dbfde299d106279a33d9f8308346022316781368591/numba-0.62.1.tar.gz", hash = "sha256:7b774242aa890e34c21200a1fc62e5b5757d5286267e71103257f4e2af0d5161", size = 2749817, upload-time = "2025-09-29T10:46:31.551Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f5/27/a5a9a58f267ec3b72f609789b2a8eefd6156bd7117e41cc9b7cf5de30490/numba-0.62.1-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:a323df9d36a0da1ca9c592a6baaddd0176d9f417ef49a65bb81951dce69d941a", size = 2684281, upload-time = "2025-09-29T10:43:31.863Z" }, + { url = "https://files.pythonhosted.org/packages/3a/9d/ffc091c0bfd7b80f66df3887a7061b6af80c8c2649902444026ee1454391/numba-0.62.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1e1f4781d3f9f7c23f16eb04e76ca10b5a3516e959634bd226fc48d5d8e7a0a", size = 2687311, upload-time = "2025-09-29T10:43:54.441Z" }, + { url = "https://files.pythonhosted.org/packages/a1/13/9a27bcd0baeea236116070c7df458414336f25e9dd5a872b066cf36b74bf/numba-0.62.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:14432af305ea68627a084cd702124fd5d0c1f5b8a413b05f4e14757202d1cf6c", size = 3734548, upload-time = "2025-09-29T10:42:38.232Z" }, + { url = "https://files.pythonhosted.org/packages/a7/00/17a1ac4a60253c784ce59549375e047da98330b82de7df6ac7f4ecc90902/numba-0.62.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f180922adf159ae36c2fe79fb94ffaa74cf5cb3688cb72dba0a904b91e978507", size = 3441277, upload-time = "2025-09-29T10:43:06.124Z" }, + { url = "https://files.pythonhosted.org/packages/86/94/20ae0ff78612c4697eaf942a639db01dd4e2d90f634ac41fa3e015c961fc/numba-0.62.1-cp310-cp310-win_amd64.whl", hash = "sha256:f41834909d411b4b8d1c68f745144136f21416547009c1e860cc2098754b4ca7", size = 2745647, upload-time = "2025-09-29T10:44:15.282Z" }, + { url = "https://files.pythonhosted.org/packages/dd/5f/8b3491dd849474f55e33c16ef55678ace1455c490555337899c35826836c/numba-0.62.1-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:f43e24b057714e480fe44bc6031de499e7cf8150c63eb461192caa6cc8530bc8", size = 2684279, upload-time = "2025-09-29T10:43:37.213Z" }, + { url = "https://files.pythonhosted.org/packages/bf/18/71969149bfeb65a629e652b752b80167fe8a6a6f6e084f1f2060801f7f31/numba-0.62.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:57cbddc53b9ee02830b828a8428757f5c218831ccc96490a314ef569d8342b7b", size = 2687330, upload-time = "2025-09-29T10:43:59.601Z" }, + { url = "https://files.pythonhosted.org/packages/0e/7d/403be3fecae33088027bc8a95dc80a2fda1e3beff3e0e5fc4374ada3afbe/numba-0.62.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:604059730c637c7885386521bb1b0ddcbc91fd56131a6dcc54163d6f1804c872", size = 3739727, upload-time = "2025-09-29T10:42:45.922Z" }, + { url = "https://files.pythonhosted.org/packages/e0/c3/3d910d08b659a6d4c62ab3cd8cd93c4d8b7709f55afa0d79a87413027ff6/numba-0.62.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d6c540880170bee817011757dc9049dba5a29db0c09b4d2349295991fe3ee55f", size = 3445490, upload-time = "2025-09-29T10:43:12.692Z" }, + { url = "https://files.pythonhosted.org/packages/5b/82/9d425c2f20d9f0a37f7cb955945a553a00fa06a2b025856c3550227c5543/numba-0.62.1-cp311-cp311-win_amd64.whl", hash = "sha256:03de6d691d6b6e2b76660ba0f38f37b81ece8b2cc524a62f2a0cfae2bfb6f9da", size = 2745550, upload-time = "2025-09-29T10:44:20.571Z" }, + { url = "https://files.pythonhosted.org/packages/5e/fa/30fa6873e9f821c0ae755915a3ca444e6ff8d6a7b6860b669a3d33377ac7/numba-0.62.1-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:1b743b32f8fa5fff22e19c2e906db2f0a340782caf024477b97801b918cf0494", size = 2685346, upload-time = "2025-09-29T10:43:43.677Z" }, + { url = "https://files.pythonhosted.org/packages/a9/d5/504ce8dc46e0dba2790c77e6b878ee65b60fe3e7d6d0006483ef6fde5a97/numba-0.62.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:90fa21b0142bcf08ad8e32a97d25d0b84b1e921bc9423f8dda07d3652860eef6", size = 2688139, upload-time = "2025-09-29T10:44:04.894Z" }, + { url = "https://files.pythonhosted.org/packages/50/5f/6a802741176c93f2ebe97ad90751894c7b0c922b52ba99a4395e79492205/numba-0.62.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6ef84d0ac19f1bf80431347b6f4ce3c39b7ec13f48f233a48c01e2ec06ecbc59", size = 3796453, upload-time = "2025-09-29T10:42:52.771Z" }, + { url = "https://files.pythonhosted.org/packages/7e/df/efd21527d25150c4544eccc9d0b7260a5dec4b7e98b5a581990e05a133c0/numba-0.62.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9315cc5e441300e0ca07c828a627d92a6802bcbf27c5487f31ae73783c58da53", size = 3496451, upload-time = "2025-09-29T10:43:19.279Z" }, + { url = "https://files.pythonhosted.org/packages/80/44/79bfdab12a02796bf4f1841630355c82b5a69933b1d50eb15c7fa37dabe8/numba-0.62.1-cp312-cp312-win_amd64.whl", hash = "sha256:44e3aa6228039992f058f5ebfcfd372c83798e9464297bdad8cc79febcf7891e", size = 2745552, upload-time = "2025-09-29T10:44:26.399Z" }, +] + +[[package]] +name = "numpy" +version = "1.26.4" +source = { registry = "https://download.pytorch.org/whl/cu126" } +sdist = { url = "https://files.pythonhosted.org/packages/65/6e/09db70a523a96d25e115e71cc56a6f9031e7b8cd166c1ac8438307c14058/numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010" } +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" }, + { url = "https://files.pythonhosted.org/packages/20/f7/b24208eba89f9d1b58c1668bc6c8c4fd472b20c45573cb767f59d49fb0f6/numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a" }, + { 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" }, + { 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" }, + { url = "https://files.pythonhosted.org/packages/24/03/6f229fe3187546435c4f6f89f6d26c129d4f5bed40552899fcf1f0bf9e50/numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a" }, + { url = "https://files.pythonhosted.org/packages/39/fe/39ada9b094f01f5a35486577c848fe274e374bbf8d8f472e1423a0bbd26d/numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2" }, + { url = "https://files.pythonhosted.org/packages/d5/ef/6ad11d51197aad206a9ad2286dc1aac6a378059e06e8cf22cd08ed4f20dc/numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07" }, + { url = "https://files.pythonhosted.org/packages/19/77/538f202862b9183f54108557bfda67e17603fc560c384559e769321c9d92/numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5" }, + { url = "https://files.pythonhosted.org/packages/11/57/baae43d14fe163fa0e4c47f307b6b2511ab8d7d30177c491960504252053/numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71" }, + { url = "https://files.pythonhosted.org/packages/1a/2e/151484f49fd03944c4a3ad9c418ed193cfd02724e138ac8a9505d056c582/numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef" }, + { url = "https://files.pythonhosted.org/packages/79/ae/7e5b85136806f9dadf4878bf73cf223fe5c2636818ba3ab1c585d0403164/numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e" }, + { url = "https://files.pythonhosted.org/packages/3a/d0/edc009c27b406c4f9cbc79274d6e46d634d139075492ad055e3d68445925/numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5" }, + { url = "https://files.pythonhosted.org/packages/09/bf/2b1aaf8f525f2923ff6cfcf134ae5e750e279ac65ebf386c75a0cf6da06a/numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a" }, + { url = "https://files.pythonhosted.org/packages/df/a0/4e0f14d847cfc2a633a1c8621d00724f3206cfeddeb66d35698c4e2cf3d2/numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a" }, + { url = "https://files.pythonhosted.org/packages/d2/b7/a734c733286e10a7f1a8ad1ae8c90f2d33bf604a96548e0a4a3a6739b468/numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20" }, + { url = "https://files.pythonhosted.org/packages/3f/6b/5610004206cf7f8e7ad91c5a85a8c71b2f2f8051a0c0c4d5916b76d6cbb2/numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2" }, + { url = "https://files.pythonhosted.org/packages/95/12/8f2020a8e8b8383ac0177dc9570aad031a3beb12e38847f7129bacd96228/numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218" }, + { url = "https://files.pythonhosted.org/packages/75/5b/ca6c8bd14007e5ca171c7c03102d17b4f4e0ceb53957e8c44343a9546dcc/numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b" }, + { url = "https://files.pythonhosted.org/packages/79/f8/97f10e6755e2a7d027ca783f63044d5b1bc1ae7acb12afe6a9b4286eac17/numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b" }, + { url = "https://files.pythonhosted.org/packages/0f/50/de23fde84e45f5c4fda2488c759b69990fd4512387a8632860f3ac9cd225/numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed" }, + { url = "https://files.pythonhosted.org/packages/4c/0c/9c603826b6465e82591e05ca230dfc13376da512b25ccd0894709b054ed0/numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a" }, + { url = "https://files.pythonhosted.org/packages/76/8c/2ba3902e1a0fc1c74962ea9bb33a534bb05984ad7ff9515bf8d07527cadd/numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0" }, + { url = "https://files.pythonhosted.org/packages/28/4a/46d9e65106879492374999e76eb85f87b15328e06bd1550668f79f7b18c6/numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110" }, + { url = "https://files.pythonhosted.org/packages/16/2e/86f24451c2d530c88daf997cb8d6ac622c1d40d19f5a031ed68a4b73a374/numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818" }, +] + +[[package]] +name = "nvidia-cublas-cu12" +version = "12.6.4.1" +source = { registry = "https://download.pytorch.org/whl/cu126" } +wheels = [ + { url = "https://pypi.nvidia.com/nvidia-cublas-cu12/nvidia_cublas_cu12-12.6.4.1-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:08ed2686e9875d01b58e3cb379c6896df8e76c75e0d4a7f7dace3d7b6d9ef8eb" }, + { url = "https://pypi.nvidia.com/nvidia-cublas-cu12/nvidia_cublas_cu12-12.6.4.1-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:235f728d6e2a409eddf1df58d5b0921cf80cfa9e72b9f2775ccb7b4a87984668" }, +] + +[[package]] +name = "nvidia-cuda-cupti-cu12" +version = "12.6.80" +source = { registry = "https://download.pytorch.org/whl/cu126" } +wheels = [ + { url = "https://pypi.nvidia.com/nvidia-cuda-cupti-cu12/nvidia_cuda_cupti_cu12-12.6.80-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:166ee35a3ff1587f2490364f90eeeb8da06cd867bd5b701bf7f9a02b78bc63fc" }, + { url = "https://pypi.nvidia.com/nvidia-cuda-cupti-cu12/nvidia_cuda_cupti_cu12-12.6.80-py3-none-manylinux2014_aarch64.whl", hash = "sha256:358b4a1d35370353d52e12f0a7d1769fc01ff74a191689d3870b2123156184c4" }, + { url = "https://pypi.nvidia.com/nvidia-cuda-cupti-cu12/nvidia_cuda_cupti_cu12-12.6.80-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6768bad6cab4f19e8292125e5f1ac8aa7d1718704012a0e3272a6f61c4bce132" }, + { url = "https://pypi.nvidia.com/nvidia-cuda-cupti-cu12/nvidia_cuda_cupti_cu12-12.6.80-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a3eff6cdfcc6a4c35db968a06fcadb061cbc7d6dde548609a941ff8701b98b73" }, +] + +[[package]] +name = "nvidia-cuda-nvrtc-cu12" +version = "12.6.85" +source = { registry = "https://download.pytorch.org/whl/cu126" } +wheels = [ + { url = "https://pypi.nvidia.com/nvidia-cuda-nvrtc-cu12/nvidia_cuda_nvrtc_cu12-12.6.85-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:800927308ccc5dd6246d3f61f7fcef2ed7ec4e59e199090d360d3293f78bd5a2" }, + { url = "https://pypi.nvidia.com/nvidia-cuda-nvrtc-cu12/nvidia_cuda_nvrtc_cu12-12.6.85-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3f3134f50963882373063901657554f230bedf6039d30b09f6be55c64c993a37" }, +] + +[[package]] +name = "nvidia-cuda-runtime-cu12" +version = "12.6.77" +source = { registry = "https://download.pytorch.org/whl/cu126" } +wheels = [ + { url = "https://pypi.nvidia.com/nvidia-cuda-runtime-cu12/nvidia_cuda_runtime_cu12-12.6.77-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6116fad3e049e04791c0256a9778c16237837c08b27ed8c8401e2e45de8d60cd" }, + { url = "https://pypi.nvidia.com/nvidia-cuda-runtime-cu12/nvidia_cuda_runtime_cu12-12.6.77-py3-none-manylinux2014_aarch64.whl", hash = "sha256:d461264ecb429c84c8879a7153499ddc7b19b5f8d84c204307491989a365588e" }, + { url = "https://pypi.nvidia.com/nvidia-cuda-runtime-cu12/nvidia_cuda_runtime_cu12-12.6.77-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ba3b56a4f896141e25e19ab287cd71e52a6a0f4b29d0d31609f60e3b4d5219b7" }, + { url = "https://pypi.nvidia.com/nvidia-cuda-runtime-cu12/nvidia_cuda_runtime_cu12-12.6.77-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a84d15d5e1da416dd4774cb42edf5e954a3e60cc945698dc1d5be02321c44dc8" }, +] + +[[package]] +name = "nvidia-cudnn-cu12" +version = "9.10.2.21" +source = { registry = "https://download.pytorch.org/whl/cu126" } +dependencies = [ + { name = "nvidia-cublas-cu12", marker = "sys_platform == 'linux'" }, +] +wheels = [ + { url = "https://pypi.nvidia.com/nvidia-cudnn-cu12/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:c9132cc3f8958447b4910a1720036d9eff5928cc3179b0a51fb6d167c6cc87d8" }, + { url = "https://pypi.nvidia.com/nvidia-cudnn-cu12/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8" }, +] + +[[package]] +name = "nvidia-cufft-cu12" +version = "11.3.0.4" +source = { registry = "https://download.pytorch.org/whl/cu126" } +dependencies = [ + { name = "nvidia-nvjitlink-cu12", marker = "sys_platform == 'linux'" }, +] +wheels = [ + { url = "https://pypi.nvidia.com/nvidia-cufft-cu12/nvidia_cufft_cu12-11.3.0.4-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d16079550df460376455cba121db6564089176d9bac9e4f360493ca4741b22a6" }, + { url = "https://pypi.nvidia.com/nvidia-cufft-cu12/nvidia_cufft_cu12-11.3.0.4-py3-none-manylinux2014_aarch64.whl", hash = "sha256:8510990de9f96c803a051822618d42bf6cb8f069ff3f48d93a8486efdacb48fb" }, + { url = "https://pypi.nvidia.com/nvidia-cufft-cu12/nvidia_cufft_cu12-11.3.0.4-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ccba62eb9cef5559abd5e0d54ceed2d9934030f51163df018532142a8ec533e5" }, + { url = "https://pypi.nvidia.com/nvidia-cufft-cu12/nvidia_cufft_cu12-11.3.0.4-py3-none-manylinux2014_x86_64.whl", hash = "sha256:768160ac89f6f7b459bee747e8d175dbf53619cfe74b2a5636264163138013ca" }, +] + +[[package]] +name = "nvidia-cufile-cu12" +version = "1.11.1.6" +source = { registry = "https://download.pytorch.org/whl/cu126" } +wheels = [ + { url = "https://pypi.nvidia.com/nvidia-cufile-cu12/nvidia_cufile_cu12-1.11.1.6-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc23469d1c7e52ce6c1d55253273d32c565dd22068647f3aa59b3c6b005bf159" }, + { url = "https://pypi.nvidia.com/nvidia-cufile-cu12/nvidia_cufile_cu12-1.11.1.6-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:8f57a0051dcf2543f6dc2b98a98cb2719c37d3cee1baba8965d57f3bbc90d4db" }, +] + +[[package]] +name = "nvidia-curand-cu12" +version = "10.3.7.77" +source = { registry = "https://download.pytorch.org/whl/cu126" } +wheels = [ + { url = "https://pypi.nvidia.com/nvidia-curand-cu12/nvidia_curand_cu12-10.3.7.77-py3-none-manylinux2014_aarch64.whl", hash = "sha256:6e82df077060ea28e37f48a3ec442a8f47690c7499bff392a5938614b56c98d8" }, + { url = "https://pypi.nvidia.com/nvidia-curand-cu12/nvidia_curand_cu12-10.3.7.77-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a42cd1344297f70b9e39a1e4f467a4e1c10f1da54ff7a85c12197f6c652c8bdf" }, + { url = "https://pypi.nvidia.com/nvidia-curand-cu12/nvidia_curand_cu12-10.3.7.77-py3-none-manylinux2014_x86_64.whl", hash = "sha256:99f1a32f1ac2bd134897fc7a203f779303261268a65762a623bf30cc9fe79117" }, + { url = "https://pypi.nvidia.com/nvidia-curand-cu12/nvidia_curand_cu12-10.3.7.77-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:7b2ed8e95595c3591d984ea3603dd66fe6ce6812b886d59049988a712ed06b6e" }, +] + +[[package]] +name = "nvidia-cusolver-cu12" +version = "11.7.1.2" +source = { registry = "https://download.pytorch.org/whl/cu126" } +dependencies = [ + { name = "nvidia-cublas-cu12", marker = "sys_platform == 'linux'" }, + { name = "nvidia-cusparse-cu12", marker = "sys_platform == 'linux'" }, + { name = "nvidia-nvjitlink-cu12", marker = "sys_platform == 'linux'" }, +] +wheels = [ + { url = "https://pypi.nvidia.com/nvidia-cusolver-cu12/nvidia_cusolver_cu12-11.7.1.2-py3-none-manylinux2014_aarch64.whl", hash = "sha256:0ce237ef60acde1efc457335a2ddadfd7610b892d94efee7b776c64bb1cac9e0" }, + { url = "https://pypi.nvidia.com/nvidia-cusolver-cu12/nvidia_cusolver_cu12-11.7.1.2-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e9e49843a7707e42022babb9bcfa33c29857a93b88020c4e4434656a655b698c" }, + { url = "https://pypi.nvidia.com/nvidia-cusolver-cu12/nvidia_cusolver_cu12-11.7.1.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:6cf28f17f64107a0c4d7802be5ff5537b2130bfc112f25d5a30df227058ca0e6" }, + { url = "https://pypi.nvidia.com/nvidia-cusolver-cu12/nvidia_cusolver_cu12-11.7.1.2-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:dbbe4fc38ec1289c7e5230e16248365e375c3673c9c8bac5796e2e20db07f56e" }, +] + +[[package]] +name = "nvidia-cusparse-cu12" +version = "12.5.4.2" +source = { registry = "https://download.pytorch.org/whl/cu126" } +dependencies = [ + { name = "nvidia-nvjitlink-cu12", marker = "sys_platform == 'linux'" }, +] +wheels = [ + { url = "https://pypi.nvidia.com/nvidia-cusparse-cu12/nvidia_cusparse_cu12-12.5.4.2-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d25b62fb18751758fe3c93a4a08eff08effedfe4edf1c6bb5afd0890fe88f887" }, + { url = "https://pypi.nvidia.com/nvidia-cusparse-cu12/nvidia_cusparse_cu12-12.5.4.2-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7aa32fa5470cf754f72d1116c7cbc300b4e638d3ae5304cfa4a638a5b87161b1" }, + { url = "https://pypi.nvidia.com/nvidia-cusparse-cu12/nvidia_cusparse_cu12-12.5.4.2-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7556d9eca156e18184b94947ade0fba5bb47d69cec46bf8660fd2c71a4b48b73" }, + { url = "https://pypi.nvidia.com/nvidia-cusparse-cu12/nvidia_cusparse_cu12-12.5.4.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:23749a6571191a215cb74d1cdbff4a86e7b19f1200c071b3fcf844a5bea23a2f" }, +] + +[[package]] +name = "nvidia-cusparselt-cu12" +version = "0.7.1" +source = { registry = "https://download.pytorch.org/whl/cu126" } +wheels = [ + { url = "https://pypi.nvidia.com/nvidia-cusparselt-cu12/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:8878dce784d0fac90131b6817b607e803c36e629ba34dc5b433471382196b6a5" }, + { url = "https://pypi.nvidia.com/nvidia-cusparselt-cu12/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f1bb701d6b930d5a7cea44c19ceb973311500847f81b634d802b7b539dc55623" }, +] + +[[package]] +name = "nvidia-nccl-cu12" +version = "2.28.9" +source = { registry = "https://download.pytorch.org/whl/cu126" } +wheels = [ + { url = "https://pypi.nvidia.com/nvidia-nccl-cu12/nvidia_nccl_cu12-2.28.9-py3-none-manylinux_2_18_aarch64.whl", hash = "sha256:50a36e01c4a090b9f9c47d92cec54964de6b9fcb3362d0e19b8ffc6323c21b60" }, + { url = "https://pypi.nvidia.com/nvidia-nccl-cu12/nvidia_nccl_cu12-2.28.9-py3-none-manylinux_2_18_x86_64.whl", hash = "sha256:485776daa8447da5da39681af455aa3b2c2586ddcf4af8772495e7c532c7e5ab" }, +] + +[[package]] +name = "nvidia-nvjitlink-cu12" +version = "12.6.85" +source = { registry = "https://download.pytorch.org/whl/cu126" } +wheels = [ + { url = "https://pypi.nvidia.com/nvidia-nvjitlink-cu12/nvidia_nvjitlink_cu12-12.6.85-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:eedc36df9e88b682efe4309aa16b5b4e78c2407eac59e8c10a6a47535164369a" }, + { url = "https://pypi.nvidia.com/nvidia-nvjitlink-cu12/nvidia_nvjitlink_cu12-12.6.85-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cf4eaa7d4b6b543ffd69d6abfb11efdeb2db48270d94dfd3a452c24150829e41" }, +] + +[[package]] +name = "nvidia-nvshmem-cu12" +version = "3.4.5" +source = { registry = "https://download.pytorch.org/whl/cu126" } +wheels = [ + { url = "https://pypi.nvidia.com/nvidia-nvshmem-cu12/nvidia_nvshmem_cu12-3.4.5-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0b48363fc6964dede448029434c6abed6c5e37f823cb43c3bcde7ecfc0457e15" }, + { url = "https://pypi.nvidia.com/nvidia-nvshmem-cu12/nvidia_nvshmem_cu12-3.4.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:042f2500f24c021db8a06c5eec2539027d57460e1c1a762055a6554f72c369bd" }, +] + +[[package]] +name = "nvidia-nvtx-cu12" +version = "12.6.77" +source = { registry = "https://download.pytorch.org/whl/cu126" } +wheels = [ + { url = "https://pypi.nvidia.com/nvidia-nvtx-cu12/nvidia_nvtx_cu12-12.6.77-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f44f8d86bb7d5629988d61c8d3ae61dddb2015dee142740536bc7481b022fe4b" }, + { url = "https://pypi.nvidia.com/nvidia-nvtx-cu12/nvidia_nvtx_cu12-12.6.77-py3-none-manylinux2014_aarch64.whl", hash = "sha256:adcaabb9d436c9761fca2b13959a2d237c5f9fd406c8e4b723c695409ff88059" }, + { url = "https://pypi.nvidia.com/nvidia-nvtx-cu12/nvidia_nvtx_cu12-12.6.77-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b90bed3df379fa79afbd21be8e04a0314336b8ae16768b58f2d34cb1d04cd7d2" }, + { url = "https://pypi.nvidia.com/nvidia-nvtx-cu12/nvidia_nvtx_cu12-12.6.77-py3-none-manylinux2014_x86_64.whl", hash = "sha256:6574241a3ec5fdc9334353ab8c479fe75841dbe8f4532a8fc97ce63503330ba1" }, +] + +[[package]] +name = "omegaconf" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "antlr4-python3-runtime" }, + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/09/48/6388f1bb9da707110532cb70ec4d2822858ddfb44f1cdf1233c20a80ea4b/omegaconf-2.3.0.tar.gz", hash = "sha256:d5d4b6d29955cc50ad50c46dc269bcd92c6e00f5f90d23ab5fee7bfca4ba4cc7", size = 3298120, upload-time = "2022-12-08T20:59:22.753Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e3/94/1843518e420fa3ed6919835845df698c7e27e183cb997394e4a670973a65/omegaconf-2.3.0-py3-none-any.whl", hash = "sha256:7b4df175cdb08ba400f45cae3bdcae7ba8365db4d165fc65fd04b050ab63b46b", size = 79500, upload-time = "2022-12-08T20:59:19.686Z" }, +] + +[[package]] +name = "opencv-python" +version = "4.11.0.86" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/17/06/68c27a523103dad5837dc5b87e71285280c4f098c60e4fe8a8db6486ab09/opencv-python-4.11.0.86.tar.gz", hash = "sha256:03d60ccae62304860d232272e4a4fda93c39d595780cb40b161b310244b736a4", size = 95171956, upload-time = "2025-01-16T13:52:24.737Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/05/4d/53b30a2a3ac1f75f65a59eb29cf2ee7207ce64867db47036ad61743d5a23/opencv_python-4.11.0.86-cp37-abi3-macosx_13_0_arm64.whl", hash = "sha256:432f67c223f1dc2824f5e73cdfcd9db0efc8710647d4e813012195dc9122a52a", size = 37326322, upload-time = "2025-01-16T13:52:25.887Z" }, + { url = "https://files.pythonhosted.org/packages/3b/84/0a67490741867eacdfa37bc18df96e08a9d579583b419010d7f3da8ff503/opencv_python-4.11.0.86-cp37-abi3-macosx_13_0_x86_64.whl", hash = "sha256:9d05ef13d23fe97f575153558653e2d6e87103995d54e6a35db3f282fe1f9c66", size = 56723197, upload-time = "2025-01-16T13:55:21.222Z" }, + { url = "https://files.pythonhosted.org/packages/f3/bd/29c126788da65c1fb2b5fb621b7fed0ed5f9122aa22a0868c5e2c15c6d23/opencv_python-4.11.0.86-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b92ae2c8852208817e6776ba1ea0d6b1e0a1b5431e971a2a0ddd2a8cc398202", size = 42230439, upload-time = "2025-01-16T13:51:35.822Z" }, + { url = "https://files.pythonhosted.org/packages/2c/8b/90eb44a40476fa0e71e05a0283947cfd74a5d36121a11d926ad6f3193cc4/opencv_python-4.11.0.86-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b02611523803495003bd87362db3e1d2a0454a6a63025dc6658a9830570aa0d", size = 62986597, upload-time = "2025-01-16T13:52:08.836Z" }, + { url = "https://files.pythonhosted.org/packages/fb/d7/1d5941a9dde095468b288d989ff6539dd69cd429dbf1b9e839013d21b6f0/opencv_python-4.11.0.86-cp37-abi3-win32.whl", hash = "sha256:810549cb2a4aedaa84ad9a1c92fbfdfc14090e2749cedf2c1589ad8359aa169b", size = 29384337, upload-time = "2025-01-16T13:52:13.549Z" }, + { url = "https://files.pythonhosted.org/packages/a4/7d/f1c30a92854540bf789e9cd5dde7ef49bbe63f855b85a2e6b3db8135c591/opencv_python-4.11.0.86-cp37-abi3-win_amd64.whl", hash = "sha256:085ad9b77c18853ea66283e98affefe2de8cc4c1f43eda4c100cf9b2721142ec", size = 39488044, upload-time = "2025-01-16T13:52:21.928Z" }, +] + +[[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 = "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 = "optuna-integration" +version = "4.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "optuna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8f/d9/bdfd6ac94dd733a4df90e9e5b59273e18978132320949c0ca111a1d579db/optuna_integration-4.8.0.tar.gz", hash = "sha256:9f1e3053500a5a6a23f302cbd7ac371f6dd1010372b4b4374c538b8988bd0603", size = 89695, upload-time = "2026-03-16T04:48:05.535Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/57/cb/c209dbb41829dc856b2bdd5b58e877a6480752bd595d4b55f4a8c514efd5/optuna_integration-4.8.0-py3-none-any.whl", hash = "sha256:bb5c038702f744386b9032b12b993779b02e39e6893c324637178df68998f33d", size = 103151, upload-time = "2026-03-16T04:48:04.232Z" }, +] + +[package.optional-dependencies] +wandb = [ + { name = "wandb" }, +] + +[[package]] +name = "orderly-set" +version = "5.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4a/88/39c83c35d5e97cc203e9e77a4f93bf87ec89cf6a22ac4818fdcc65d66584/orderly_set-5.5.0.tar.gz", hash = "sha256:e87185c8e4d8afa64e7f8160ee2c542a475b738bc891dc3f58102e654125e6ce", size = 27414, upload-time = "2025-07-10T20:10:55.885Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/27/fb8d7338b4d551900fa3e580acbe7a0cf655d940e164cb5c00ec31961094/orderly_set-5.5.0-py3-none-any.whl", hash = "sha256:46f0b801948e98f427b412fcabb831677194c05c3b699b80de260374baa0b1e7", size = 13068, upload-time = "2025-07-10T20:10:54.377Z" }, +] + +[[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, upload-time = "2025-10-24T15:50:38.027Z" } +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, upload-time = "2025-10-24T15:48:28.908Z" }, + { 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, upload-time = "2025-10-24T15:48:31.361Z" }, + { 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, upload-time = "2025-10-24T15:48:32.71Z" }, + { 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, upload-time = "2025-10-24T15:48:34.128Z" }, + { 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, upload-time = "2025-10-24T15:48:35.654Z" }, + { 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, upload-time = "2025-10-24T15:48:37.483Z" }, + { 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, upload-time = "2025-10-24T15:48:38.624Z" }, + { 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, upload-time = "2025-10-24T15:48:39.756Z" }, + { 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, upload-time = "2025-10-24T15:48:41.198Z" }, + { 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, upload-time = "2025-10-24T15:48:42.664Z" }, + { 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, upload-time = "2025-10-24T15:48:44.042Z" }, + { url = "https://files.pythonhosted.org/packages/ef/0e/526db1395ccb74c3d59ac1660b9a325017096dc5643086b38f27662b4add/orjson-3.11.4-cp310-cp310-win32.whl", hash = "sha256:fa9627eba4e82f99ca6d29bc967f09aba446ee2b5a1ea728949ede73d313f5d3", size = 135955, upload-time = "2025-10-24T15:48:45.495Z" }, + { url = "https://files.pythonhosted.org/packages/e6/69/18a778c9de3702b19880e73c9866b91cc85f904b885d816ba1ab318b223c/orjson-3.11.4-cp310-cp310-win_amd64.whl", hash = "sha256:23ef7abc7fca96632d8174ac115e668c1e931b8fe4dde586e92a500bf1914dcc", size = 131577, upload-time = "2025-10-24T15:48:46.609Z" }, + { url = "https://files.pythonhosted.org/packages/63/1d/1ea6005fffb56715fd48f632611e163d1604e8316a5bad2288bee9a1c9eb/orjson-3.11.4-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:5e59d23cd93ada23ec59a96f215139753fbfe3a4d989549bcb390f8c00370b39", size = 243498, upload-time = "2025-10-24T15:48:48.101Z" }, + { url = "https://files.pythonhosted.org/packages/37/d7/ffed10c7da677f2a9da307d491b9eb1d0125b0307019c4ad3d665fd31f4f/orjson-3.11.4-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:5c3aedecfc1beb988c27c79d52ebefab93b6c3921dbec361167e6559aba2d36d", size = 128961, upload-time = "2025-10-24T15:48:49.571Z" }, + { url = "https://files.pythonhosted.org/packages/a2/96/3e4d10a18866d1368f73c8c44b7fe37cc8a15c32f2a7620be3877d4c55a3/orjson-3.11.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da9e5301f1c2caa2a9a4a303480d79c9ad73560b2e7761de742ab39fe59d9175", size = 130321, upload-time = "2025-10-24T15:48:50.713Z" }, + { url = "https://files.pythonhosted.org/packages/eb/1f/465f66e93f434f968dd74d5b623eb62c657bdba2332f5a8be9f118bb74c7/orjson-3.11.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8873812c164a90a79f65368f8f96817e59e35d0cc02786a5356f0e2abed78040", size = 129207, upload-time = "2025-10-24T15:48:52.193Z" }, + { url = "https://files.pythonhosted.org/packages/28/43/d1e94837543321c119dff277ae8e348562fe8c0fafbb648ef7cb0c67e521/orjson-3.11.4-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5d7feb0741ebb15204e748f26c9638e6665a5fa93c37a2c73d64f1669b0ddc63", size = 136323, upload-time = "2025-10-24T15:48:54.806Z" }, + { url = "https://files.pythonhosted.org/packages/bf/04/93303776c8890e422a5847dd012b4853cdd88206b8bbd3edc292c90102d1/orjson-3.11.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:01ee5487fefee21e6910da4c2ee9eef005bee568a0879834df86f888d2ffbdd9", size = 137440, upload-time = "2025-10-24T15:48:56.326Z" }, + { url = "https://files.pythonhosted.org/packages/1e/ef/75519d039e5ae6b0f34d0336854d55544ba903e21bf56c83adc51cd8bf82/orjson-3.11.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d40d46f348c0321df01507f92b95a377240c4ec31985225a6668f10e2676f9a", size = 136680, upload-time = "2025-10-24T15:48:57.476Z" }, + { url = "https://files.pythonhosted.org/packages/b5/18/bf8581eaae0b941b44efe14fee7b7862c3382fbc9a0842132cfc7cf5ecf4/orjson-3.11.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95713e5fc8af84d8edc75b785d2386f653b63d62b16d681687746734b4dfc0be", size = 136160, upload-time = "2025-10-24T15:48:59.631Z" }, + { url = "https://files.pythonhosted.org/packages/c4/35/a6d582766d351f87fc0a22ad740a641b0a8e6fc47515e8614d2e4790ae10/orjson-3.11.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ad73ede24f9083614d6c4ca9a85fe70e33be7bf047ec586ee2363bc7418fe4d7", size = 140318, upload-time = "2025-10-24T15:49:00.834Z" }, + { url = "https://files.pythonhosted.org/packages/76/b3/5a4801803ab2e2e2d703bce1a56540d9f99a9143fbec7bf63d225044fef8/orjson-3.11.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:842289889de515421f3f224ef9c1f1efb199a32d76d8d2ca2706fa8afe749549", size = 406330, upload-time = "2025-10-24T15:49:02.327Z" }, + { url = "https://files.pythonhosted.org/packages/80/55/a8f682f64833e3a649f620eafefee175cbfeb9854fc5b710b90c3bca45df/orjson-3.11.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3b2427ed5791619851c52a1261b45c233930977e7de8cf36de05636c708fa905", size = 149580, upload-time = "2025-10-24T15:49:03.517Z" }, + { url = "https://files.pythonhosted.org/packages/ad/e4/c132fa0c67afbb3eb88274fa98df9ac1f631a675e7877037c611805a4413/orjson-3.11.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3c36e524af1d29982e9b190573677ea02781456b2e537d5840e4538a5ec41907", size = 139846, upload-time = "2025-10-24T15:49:04.761Z" }, + { url = "https://files.pythonhosted.org/packages/54/06/dc3491489efd651fef99c5908e13951abd1aead1257c67f16135f95ce209/orjson-3.11.4-cp311-cp311-win32.whl", hash = "sha256:87255b88756eab4a68ec61837ca754e5d10fa8bc47dc57f75cedfeaec358d54c", size = 135781, upload-time = "2025-10-24T15:49:05.969Z" }, + { url = "https://files.pythonhosted.org/packages/79/b7/5e5e8d77bd4ea02a6ac54c42c818afb01dd31961be8a574eb79f1d2cfb1e/orjson-3.11.4-cp311-cp311-win_amd64.whl", hash = "sha256:e2d5d5d798aba9a0e1fede8d853fa899ce2cb930ec0857365f700dffc2c7af6a", size = 131391, upload-time = "2025-10-24T15:49:07.355Z" }, + { url = "https://files.pythonhosted.org/packages/0f/dc/9484127cc1aa213be398ed735f5f270eedcb0c0977303a6f6ddc46b60204/orjson-3.11.4-cp311-cp311-win_arm64.whl", hash = "sha256:6bb6bb41b14c95d4f2702bce9975fda4516f1db48e500102fc4d8119032ff045", size = 126252, upload-time = "2025-10-24T15:49:08.869Z" }, + { url = "https://files.pythonhosted.org/packages/63/51/6b556192a04595b93e277a9ff71cd0cc06c21a7df98bcce5963fa0f5e36f/orjson-3.11.4-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:d4371de39319d05d3f482f372720b841c841b52f5385bd99c61ed69d55d9ab50", size = 243571, upload-time = "2025-10-24T15:49:10.008Z" }, + { url = "https://files.pythonhosted.org/packages/1c/2c/2602392ddf2601d538ff11848b98621cd465d1a1ceb9db9e8043181f2f7b/orjson-3.11.4-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:e41fd3b3cac850eaae78232f37325ed7d7436e11c471246b87b2cd294ec94853", size = 128891, upload-time = "2025-10-24T15:49:11.297Z" }, + { url = "https://files.pythonhosted.org/packages/4e/47/bf85dcf95f7a3a12bf223394a4f849430acd82633848d52def09fa3f46ad/orjson-3.11.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:600e0e9ca042878c7fdf189cf1b028fe2c1418cc9195f6cb9824eb6ed99cb938", size = 130137, upload-time = "2025-10-24T15:49:12.544Z" }, + { url = "https://files.pythonhosted.org/packages/b4/4d/a0cb31007f3ab6f1fd2a1b17057c7c349bc2baf8921a85c0180cc7be8011/orjson-3.11.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7bbf9b333f1568ef5da42bc96e18bf30fd7f8d54e9ae066d711056add508e415", size = 129152, upload-time = "2025-10-24T15:49:13.754Z" }, + { url = "https://files.pythonhosted.org/packages/f7/ef/2811def7ce3d8576b19e3929fff8f8f0d44bc5eb2e0fdecb2e6e6cc6c720/orjson-3.11.4-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4806363144bb6e7297b8e95870e78d30a649fdc4e23fc84daa80c8ebd366ce44", size = 136834, upload-time = "2025-10-24T15:49:15.307Z" }, + { url = "https://files.pythonhosted.org/packages/00/d4/9aee9e54f1809cec8ed5abd9bc31e8a9631d19460e3b8470145d25140106/orjson-3.11.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad355e8308493f527d41154e9053b86a5be892b3b359a5c6d5d95cda23601cb2", size = 137519, upload-time = "2025-10-24T15:49:16.557Z" }, + { url = "https://files.pythonhosted.org/packages/db/ea/67bfdb5465d5679e8ae8d68c11753aaf4f47e3e7264bad66dc2f2249e643/orjson-3.11.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c8a7517482667fb9f0ff1b2f16fe5829296ed7a655d04d68cd9711a4d8a4e708", size = 136749, upload-time = "2025-10-24T15:49:17.796Z" }, + { url = "https://files.pythonhosted.org/packages/01/7e/62517dddcfce6d53a39543cd74d0dccfcbdf53967017c58af68822100272/orjson-3.11.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97eb5942c7395a171cbfecc4ef6701fc3c403e762194683772df4c54cfbb2210", size = 136325, upload-time = "2025-10-24T15:49:19.347Z" }, + { url = "https://files.pythonhosted.org/packages/18/ae/40516739f99ab4c7ec3aaa5cc242d341fcb03a45d89edeeaabc5f69cb2cf/orjson-3.11.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:149d95d5e018bdd822e3f38c103b1a7c91f88d38a88aada5c4e9b3a73a244241", size = 140204, upload-time = "2025-10-24T15:49:20.545Z" }, + { url = "https://files.pythonhosted.org/packages/82/18/ff5734365623a8916e3a4037fcef1cd1782bfc14cf0992afe7940c5320bf/orjson-3.11.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:624f3951181eb46fc47dea3d221554e98784c823e7069edb5dbd0dc826ac909b", size = 406242, upload-time = "2025-10-24T15:49:21.884Z" }, + { url = "https://files.pythonhosted.org/packages/e1/43/96436041f0a0c8c8deca6a05ebeaf529bf1de04839f93ac5e7c479807aec/orjson-3.11.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:03bfa548cf35e3f8b3a96c4e8e41f753c686ff3d8e182ce275b1751deddab58c", size = 150013, upload-time = "2025-10-24T15:49:23.185Z" }, + { url = "https://files.pythonhosted.org/packages/1b/48/78302d98423ed8780479a1e682b9aecb869e8404545d999d34fa486e573e/orjson-3.11.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:525021896afef44a68148f6ed8a8bf8375553d6066c7f48537657f64823565b9", size = 139951, upload-time = "2025-10-24T15:49:24.428Z" }, + { url = "https://files.pythonhosted.org/packages/4a/7b/ad613fdcdaa812f075ec0875143c3d37f8654457d2af17703905425981bf/orjson-3.11.4-cp312-cp312-win32.whl", hash = "sha256:b58430396687ce0f7d9eeb3dd47761ca7d8fda8e9eb92b3077a7a353a75efefa", size = 136049, upload-time = "2025-10-24T15:49:25.973Z" }, + { url = "https://files.pythonhosted.org/packages/b9/3c/9cf47c3ff5f39b8350fb21ba65d789b6a1129d4cbb3033ba36c8a9023520/orjson-3.11.4-cp312-cp312-win_amd64.whl", hash = "sha256:c6dbf422894e1e3c80a177133c0dda260f81428f9de16d61041949f6a2e5c140", size = 131461, upload-time = "2025-10-24T15:49:27.259Z" }, + { url = "https://files.pythonhosted.org/packages/c6/3b/e2425f61e5825dc5b08c2a5a2b3af387eaaca22a12b9c8c01504f8614c36/orjson-3.11.4-cp312-cp312-win_arm64.whl", hash = "sha256:d38d2bc06d6415852224fcc9c0bfa834c25431e466dc319f0edd56cca81aa96e", size = 126167, upload-time = "2025-10-24T15:49:28.511Z" }, +] + +[[package]] +name = "packaging" +version = "24.1" +source = { registry = "https://download.pytorch.org/whl/cu126" } +wheels = [ + { url = "https://download.pytorch.org/whl/packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124" }, +] + +[[package]] +name = "pandas" +version = "2.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "python-dateutil" }, + { name = "pytz" }, + { name = "tzdata" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223, upload-time = "2025-09-29T23:34:51.853Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/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/c1/fa/7ac648108144a095b4fb6aa3de1954689f7af60a14cf25583f4960ecb878/pandas-2.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:602b8615ebcc4a0c1751e71840428ddebeb142ec02c786e8ad6b1ce3c8dec523", size = 11578790, upload-time = "2025-09-29T23:18:30.065Z" }, + { url = "https://files.pythonhosted.org/packages/9b/35/74442388c6cf008882d4d4bdfc4109be87e9b8b7ccd097ad1e7f006e2e95/pandas-2.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8fe25fc7b623b0ef6b5009149627e34d2a4657e880948ec3c840e9402e5c1b45", size = 10833831, upload-time = "2025-09-29T23:38:56.071Z" }, + { url = "https://files.pythonhosted.org/packages/fe/e4/de154cbfeee13383ad58d23017da99390b91d73f8c11856f2095e813201b/pandas-2.3.3-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b468d3dad6ff947df92dcb32ede5b7bd41a9b3cceef0a30ed925f6d01fb8fa66", size = 12199267, upload-time = "2025-09-29T23:18:41.627Z" }, + { url = "https://files.pythonhosted.org/packages/bf/c9/63f8d545568d9ab91476b1818b4741f521646cbdd151c6efebf40d6de6f7/pandas-2.3.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b98560e98cb334799c0b07ca7967ac361a47326e9b4e5a7dfb5ab2b1c9d35a1b", size = 12789281, upload-time = "2025-09-29T23:18:56.834Z" }, + { url = "https://files.pythonhosted.org/packages/f2/00/a5ac8c7a0e67fd1a6059e40aa08fa1c52cc00709077d2300e210c3ce0322/pandas-2.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37b5848ba49824e5c30bedb9c830ab9b7751fd049bc7914533e01c65f79791", size = 13240453, upload-time = "2025-09-29T23:19:09.247Z" }, + { url = "https://files.pythonhosted.org/packages/27/4d/5c23a5bc7bd209231618dd9e606ce076272c9bc4f12023a70e03a86b4067/pandas-2.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:db4301b2d1f926ae677a751eb2bd0e8c5f5319c9cb3f88b0becbbb0b07b34151", size = 13890361, upload-time = "2025-09-29T23:19:25.342Z" }, + { url = "https://files.pythonhosted.org/packages/8e/59/712db1d7040520de7a4965df15b774348980e6df45c129b8c64d0dbe74ef/pandas-2.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:f086f6fe114e19d92014a1966f43a3e62285109afe874f067f5abbdcbb10e59c", size = 11348702, upload-time = "2025-09-29T23:19:38.296Z" }, + { url = "https://files.pythonhosted.org/packages/9c/fb/231d89e8637c808b997d172b18e9d4a4bc7bf31296196c260526055d1ea0/pandas-2.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d21f6d74eb1725c2efaa71a2bfc661a0689579b58e9c0ca58a739ff0b002b53", size = 11597846, upload-time = "2025-09-29T23:19:48.856Z" }, + { url = "https://files.pythonhosted.org/packages/5c/bd/bf8064d9cfa214294356c2d6702b716d3cf3bb24be59287a6a21e24cae6b/pandas-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3fd2f887589c7aa868e02632612ba39acb0b8948faf5cc58f0850e165bd46f35", size = 10729618, upload-time = "2025-09-29T23:39:08.659Z" }, + { url = "https://files.pythonhosted.org/packages/57/56/cf2dbe1a3f5271370669475ead12ce77c61726ffd19a35546e31aa8edf4e/pandas-2.3.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecaf1e12bdc03c86ad4a7ea848d66c685cb6851d807a26aa245ca3d2017a1908", size = 11737212, upload-time = "2025-09-29T23:19:59.765Z" }, + { url = "https://files.pythonhosted.org/packages/e5/63/cd7d615331b328e287d8233ba9fdf191a9c2d11b6af0c7a59cfcec23de68/pandas-2.3.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b3d11d2fda7eb164ef27ffc14b4fcab16a80e1ce67e9f57e19ec0afaf715ba89", size = 12362693, upload-time = "2025-09-29T23:20:14.098Z" }, + { url = "https://files.pythonhosted.org/packages/a6/de/8b1895b107277d52f2b42d3a6806e69cfef0d5cf1d0ba343470b9d8e0a04/pandas-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a68e15f780eddf2b07d242e17a04aa187a7ee12b40b930bfdd78070556550e98", size = 12771002, upload-time = "2025-09-29T23:20:26.76Z" }, + { url = "https://files.pythonhosted.org/packages/87/21/84072af3187a677c5893b170ba2c8fbe450a6ff911234916da889b698220/pandas-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:371a4ab48e950033bcf52b6527eccb564f52dc826c02afd9a1bc0ab731bba084", size = 13450971, upload-time = "2025-09-29T23:20:41.344Z" }, + { url = "https://files.pythonhosted.org/packages/86/41/585a168330ff063014880a80d744219dbf1dd7a1c706e75ab3425a987384/pandas-2.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:a16dcec078a01eeef8ee61bf64074b4e524a2a3f4b3be9326420cabe59c4778b", size = 10992722, upload-time = "2025-09-29T23:20:54.139Z" }, +] + +[[package]] +name = "pillow" +version = "12.0.0" +source = { registry = "https://download.pytorch.org/whl/cu126" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/cace85a1b0c9775a9f8f5d5423c8261c858760e2466c79b2dd184638b056/pillow-12.0.0.tar.gz", hash = "sha256:87d4f8125c9988bfbed67af47dd7a953e2fc7b0cc1e7800ec6d2080d490bb353" } +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" }, + { url = "https://files.pythonhosted.org/packages/cb/e9/4e58fb097fb74c7b4758a680aacd558810a417d1edaa7000142976ef9d2f/pillow-12.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1ac11e8ea4f611c3c0147424eae514028b5e9077dd99ab91e1bd7bc33ff145e1" }, + { 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" }, + { 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" }, + { 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" }, + { 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" }, + { url = "https://files.pythonhosted.org/packages/28/03/96f718331b19b355610ef4ebdbbde3557c726513030665071fd025745671/pillow-12.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:32ed80ea8a90ee3e6fa08c21e2e091bba6eda8eccc83dbc34c95169507a91f10" }, + { url = "https://files.pythonhosted.org/packages/3a/a0/6a193b3f0cc9437b122978d2c5cbce59510ccf9a5b48825096ed7472da2f/pillow-12.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c828a1ae702fc712978bda0320ba1b9893d99be0badf2647f693cc01cf0f04fa" }, + { url = "https://files.pythonhosted.org/packages/a7/c4/043192375eaa4463254e8e61f0e2ec9a846b983929a8d0a7122e0a6d6fff/pillow-12.0.0-cp310-cp310-win32.whl", hash = "sha256:bd87e140e45399c818fac4247880b9ce719e4783d767e030a883a970be632275" }, + { url = "https://files.pythonhosted.org/packages/92/c6/c2f2fc7e56301c21827e689bb8b0b465f1b52878b57471a070678c0c33cd/pillow-12.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:455247ac8a4cfb7b9bc45b7e432d10421aea9fc2e74d285ba4072688a74c2e9d" }, + { url = "https://files.pythonhosted.org/packages/b2/d2/5f675067ba82da7a1c238a73b32e3fd78d67f9d9f80fbadd33a40b9c0481/pillow-12.0.0-cp310-cp310-win_arm64.whl", hash = "sha256:6ace95230bfb7cd79ef66caa064bbe2f2a1e63d93471c3a2e1f1348d9f22d6b7" }, + { url = "https://files.pythonhosted.org/packages/0e/5a/a2f6773b64edb921a756eb0729068acad9fc5208a53f4a349396e9436721/pillow-12.0.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:0fd00cac9c03256c8b2ff58f162ebcd2587ad3e1f2e397eab718c47e24d231cc" }, + { url = "https://files.pythonhosted.org/packages/2e/05/069b1f8a2e4b5a37493da6c5868531c3f77b85e716ad7a590ef87d58730d/pillow-12.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3475b96f5908b3b16c47533daaa87380c491357d197564e0ba34ae75c0f3257" }, + { url = "https://files.pythonhosted.org/packages/61/e3/2c820d6e9a36432503ead175ae294f96861b07600a7156154a086ba7111a/pillow-12.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:110486b79f2d112cf6add83b28b627e369219388f64ef2f960fef9ebaf54c642" }, + { url = "https://files.pythonhosted.org/packages/4f/89/63427f51c64209c5e23d4d52071c8d0f21024d3a8a487737caaf614a5795/pillow-12.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5269cc1caeedb67e6f7269a42014f381f45e2e7cd42d834ede3c703a1d915fe3" }, + { url = "https://files.pythonhosted.org/packages/f6/1b/c9711318d4901093c15840f268ad649459cd81984c9ec9887756cca049a5/pillow-12.0.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa5129de4e174daccbc59d0a3b6d20eaf24417d59851c07ebb37aeb02947987c" }, + { url = "https://files.pythonhosted.org/packages/41/1e/db9470f2d030b4995083044cd8738cdd1bf773106819f6d8ba12597d5352/pillow-12.0.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bee2a6db3a7242ea309aa7ee8e2780726fed67ff4e5b40169f2c940e7eb09227" }, + { url = "https://files.pythonhosted.org/packages/cc/b0/6177a8bdd5ee4ed87cba2de5a3cc1db55ffbbec6176784ce5bb75aa96798/pillow-12.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:90387104ee8400a7b4598253b4c406f8958f59fcf983a6cea2b50d59f7d63d0b" }, + { url = "https://files.pythonhosted.org/packages/bc/5e/61537aa6fa977922c6a03253a0e727e6e4a72381a80d63ad8eec350684f2/pillow-12.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bc91a56697869546d1b8f0a3ff35224557ae7f881050e99f615e0119bf934b4e" }, + { url = "https://files.pythonhosted.org/packages/1f/3d/d5033539344ee3cbd9a4d69e12e63ca3a44a739eb2d4c8da350a3d38edd7/pillow-12.0.0-cp311-cp311-win32.whl", hash = "sha256:27f95b12453d165099c84f8a8bfdfd46b9e4bda9e0e4b65f0635430027f55739" }, + { url = "https://files.pythonhosted.org/packages/4d/42/aaca386de5cc8bd8a0254516957c1f265e3521c91515b16e286c662854c4/pillow-12.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:b583dc9070312190192631373c6c8ed277254aa6e6084b74bdd0a6d3b221608e" }, + { url = "https://files.pythonhosted.org/packages/ba/f1/9197c9c2d5708b785f631a6dfbfa8eb3fb9672837cb92ae9af812c13b4ed/pillow-12.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:759de84a33be3b178a64c8ba28ad5c135900359e85fb662bc6e403ad4407791d" }, + { url = "https://files.pythonhosted.org/packages/2c/90/4fcce2c22caf044e660a198d740e7fbc14395619e3cb1abad12192c0826c/pillow-12.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:53561a4ddc36facb432fae7a9d8afbfaf94795414f5cdc5fc52f28c1dca90371" }, + { url = "https://files.pythonhosted.org/packages/fd/e0/ed960067543d080691d47d6938ebccbf3976a931c9567ab2fbfab983a5dd/pillow-12.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:71db6b4c1653045dacc1585c1b0d184004f0d7e694c7b34ac165ca70c0838082" }, + { url = "https://files.pythonhosted.org/packages/e7/a1/f81fdeddcb99c044bf7d6faa47e12850f13cee0849537a7d27eeab5534d4/pillow-12.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2fa5f0b6716fc88f11380b88b31fe591a06c6315e955c096c35715788b339e3f" }, + { url = "https://files.pythonhosted.org/packages/88/e1/9098d3ce341a8750b55b0e00c03f1630d6178f38ac191c81c97a3b047b44/pillow-12.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:82240051c6ca513c616f7f9da06e871f61bfd7805f566275841af15015b8f98d" }, + { url = "https://files.pythonhosted.org/packages/a7/62/a22e8d3b602ae8cc01446d0c57a54e982737f44b6f2e1e019a925143771d/pillow-12.0.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:55f818bd74fe2f11d4d7cbc65880a843c4075e0ac7226bc1a23261dbea531953" }, + { url = "https://files.pythonhosted.org/packages/4f/87/424511bdcd02c8d7acf9f65caa09f291a519b16bd83c3fb3374b3d4ae951/pillow-12.0.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b87843e225e74576437fd5b6a4c2205d422754f84a06942cfaf1dc32243e45a8" }, + { url = "https://files.pythonhosted.org/packages/dc/4d/435c8ac688c54d11755aedfdd9f29c9eeddf68d150fe42d1d3dbd2365149/pillow-12.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c607c90ba67533e1b2355b821fef6764d1dd2cbe26b8c1005ae84f7aea25ff79" }, + { url = "https://files.pythonhosted.org/packages/2b/f2/ad34167a8059a59b8ad10bc5c72d4d9b35acc6b7c0877af8ac885b5f2044/pillow-12.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:21f241bdd5080a15bc86d3466a9f6074a9c2c2b314100dd896ac81ee6db2f1ba" }, + { url = "https://files.pythonhosted.org/packages/0c/b1/a7391df6adacf0a5c2cf6ac1cf1fcc1369e7d439d28f637a847f8803beb3/pillow-12.0.0-cp312-cp312-win32.whl", hash = "sha256:dd333073e0cacdc3089525c7df7d39b211bcdf31fc2824e49d01c6b6187b07d0" }, + { url = "https://files.pythonhosted.org/packages/a2/0b/d87733741526541c909bbf159e338dcace4f982daac6e5a8d6be225ca32d/pillow-12.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:9fe611163f6303d1619bbcb653540a4d60f9e55e622d60a3108be0d5b441017a" }, + { url = "https://files.pythonhosted.org/packages/bc/96/aaa61ce33cc98421fb6088af2a03be4157b1e7e0e87087c888e2370a7f45/pillow-12.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:7dfb439562f234f7d57b1ac6bc8fe7f838a4bd49c79230e0f6a1da93e82f1fad" }, + { url = "https://files.pythonhosted.org/packages/1d/b3/582327e6c9f86d037b63beebe981425d6811104cb443e8193824ef1a2f27/pillow-12.0.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b22bd8c974942477156be55a768f7aa37c46904c175be4e158b6a86e3a6b7ca8" }, + { url = "https://files.pythonhosted.org/packages/fd/d6/67748211d119f3b6540baf90f92fae73ae51d5217b171b0e8b5f7e5d558f/pillow-12.0.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:805ebf596939e48dbb2e4922a1d3852cfc25c38160751ce02da93058b48d252a" }, + { url = "https://files.pythonhosted.org/packages/2d/e1/f8281e5d844c41872b273b9f2c34a4bf64ca08905668c8ae730eedc7c9fa/pillow-12.0.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cae81479f77420d217def5f54b5b9d279804d17e982e0f2fa19b1d1e14ab5197" }, + { url = "https://files.pythonhosted.org/packages/94/5a/0d8ab8ffe8a102ff5df60d0de5af309015163bf710c7bb3e8311dd3b3ad0/pillow-12.0.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:aeaefa96c768fc66818730b952a862235d68825c178f1b3ffd4efd7ad2edcb7c" }, + { url = "https://files.pythonhosted.org/packages/20/2e/3434380e8110b76cd9eb00a363c484b050f949b4bbe84ba770bb8508a02c/pillow-12.0.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:09f2d0abef9e4e2f349305a4f8cc784a8a6c2f58a8c4892eea13b10a943bd26e" }, + { url = "https://files.pythonhosted.org/packages/57/ca/5a9d38900d9d74785141d6580950fe705de68af735ff6e727cb911b64740/pillow-12.0.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bdee52571a343d721fb2eb3b090a82d959ff37fc631e3f70422e0c2e029f3e76" }, + { url = "https://files.pythonhosted.org/packages/95/7e/f896623c3c635a90537ac093c6a618ebe1a90d87206e42309cb5d98a1b9e/pillow-12.0.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:b290fd8aa38422444d4b50d579de197557f182ef1068b75f5aa8558638b8d0a5" }, +] + +[[package]] +name = "platformdirs" +version = "4.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/61/33/9611380c2bdb1225fdef633e2a9610622310fed35ab11dac9620972ee088/platformdirs-4.5.0.tar.gz", hash = "sha256:70ddccdd7c99fc5942e9fc25636a8b34d04c24b335100223152c2803e4063312", size = 21632, upload-time = "2025-10-08T17:44:48.791Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/73/cb/ac7874b3e5d58441674fb70742e6c374b28b0c7cb988d37d991cde47166c/platformdirs-4.5.0-py3-none-any.whl", hash = "sha256:e578a81bb873cbb89a41fcc904c7ef523cc18284b7e3b3ccf06aca1403b7ebd3", size = 18651, upload-time = "2025-10-08T17:44:47.223Z" }, +] + +[[package]] +name = "plotly" +version = "6.5.0" +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" } +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" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "pre-commit" +version = "4.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cfgv" }, + { name = "identify" }, + { name = "nodeenv" }, + { 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, upload-time = "2025-11-22T21:02:42.304Z" } +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, upload-time = "2025-11-22T21:02:40.836Z" }, +] + +[[package]] +name = "preshed" +version = "3.0.12" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cymem" }, + { name = "murmurhash" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bf/34/eb4f5f0f678e152a96e826da867d2f41c4b18a2d589e40e1dd3347219e91/preshed-3.0.12.tar.gz", hash = "sha256:b73f9a8b54ee1d44529cc6018356896cff93d48f755f29c134734d9371c0d685", size = 15027, upload-time = "2025-11-17T13:00:33.621Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/d0/1245d6d89b051dd5356ffaaa43da05408f37d2da4cfadcf77356ba46da4f/preshed-3.0.12-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d8f0bc207bb5bfe69e3a232367c264cac900dc14e9219cd061b98eaca9e7da61", size = 128866, upload-time = "2025-11-17T12:59:06.633Z" }, + { url = "https://files.pythonhosted.org/packages/24/24/f06650f22450888434a51b17971b650186d2e68f5eaf292e6e8e4be7974c/preshed-3.0.12-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0c8a8d571c044ddab5369d30d172c87545f44daa1510bde92b7e0144a8f4f92b", size = 124848, upload-time = "2025-11-17T12:59:08.641Z" }, + { url = "https://files.pythonhosted.org/packages/88/a1/78bdd4938c3286998c0609491c4a0a8aee2f4de4003364112c295a2f32b8/preshed-3.0.12-cp310-cp310-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6cca080ac9bbc978625c8f0c56ef17471162193c7c1a4622fbde7721da1bdd40", size = 780279, upload-time = "2025-11-17T12:59:10.009Z" }, + { url = "https://files.pythonhosted.org/packages/8f/f8/6fbf083346a007927a9e4ce3686ae54ba74191e74fc3af34863ea7be9dea/preshed-3.0.12-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cfd3672007c7b7cac554a0e5f263d7bc94109dc508ee1ef43b2f6ec8c2e2e9e8", size = 781954, upload-time = "2025-11-17T12:59:11.574Z" }, + { url = "https://files.pythonhosted.org/packages/91/c3/f28c7a6cc03e85002780b75249c3557c0fe503792ac66a7b9c5379569999/preshed-3.0.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e01609074713aba93a8143480e67942fbe6898fe134b98d813819bec42a8cae7", size = 799772, upload-time = "2025-11-17T12:59:14.371Z" }, + { url = "https://files.pythonhosted.org/packages/46/25/ca22fa0db162e286db7a94a4f08c1ceb4872d3d64610b807148935ae084c/preshed-3.0.12-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:30d8a53015663b0d666012bc10d22e8bdd7359191d84a8980ae902e0b87caf24", size = 820532, upload-time = "2025-11-17T12:59:16.281Z" }, + { url = "https://files.pythonhosted.org/packages/0f/57/459a6eea7e15034756f4c2650a9aba6d023aa7976748b18476bd4c0b6fef/preshed-3.0.12-cp310-cp310-win_amd64.whl", hash = "sha256:bf2235bbe09b4862b914086f37a065cc84259e1b53c8ed996cbbd6519ea36b62", size = 117482, upload-time = "2025-11-17T12:59:18.36Z" }, + { url = "https://files.pythonhosted.org/packages/80/1f/a7b648a57d259891bd9b2c8ef1978622fa37b46a9368f054881488b9b4fe/preshed-3.0.12-cp310-cp310-win_arm64.whl", hash = "sha256:139d08b10693bfccb0ea000f47dcca5fc4a78fc1b96c1832c920be9b0a4c8f04", size = 105504, upload-time = "2025-11-17T12:59:19.562Z" }, + { url = "https://files.pythonhosted.org/packages/1e/54/d1e02d0a0ea348fb6a769506166e366abfe87ee917c2f11f7139c7acbf10/preshed-3.0.12-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bc45fda3fd4ae1ae15c37f18f0777cf389ce9184ef8884b39b18894416fd1341", size = 128439, upload-time = "2025-11-17T12:59:21.317Z" }, + { url = "https://files.pythonhosted.org/packages/8c/cb/685ca57ca6e438345b3f6c20226705a0e056a3de399a5bf8a9ee89b3dd2b/preshed-3.0.12-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75d6e628bc78c022dbb9267242715718f862c3105927732d166076ff009d65de", size = 124544, upload-time = "2025-11-17T12:59:22.944Z" }, + { url = "https://files.pythonhosted.org/packages/f8/07/018fcd3bf298304e1570065cf80601ac16acd29f799578fd47b715dd3ca2/preshed-3.0.12-cp311-cp311-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b901cff5c814facf7a864b0a4c14a16d45fa1379899a585b3fb48ee36a2dccdb", size = 824728, upload-time = "2025-11-17T12:59:24.614Z" }, + { url = "https://files.pythonhosted.org/packages/79/dc/d888b328fcedae530df53396d9fc0006026aa8793fec54d7d34f57f31ff5/preshed-3.0.12-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d1099253bf73dd3c39313280bd5331841f769637b27ddb576ff362c4e7bad298", size = 825969, upload-time = "2025-11-17T12:59:26.493Z" }, + { url = "https://files.pythonhosted.org/packages/21/51/f19933301f42ece1ffef1f7f4c370d09f0351c43c528e66fac24560e44d2/preshed-3.0.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1af4a049ffe9d0246e5dc10d6f54820ed064c40e5c3f7b6526127c664008297c", size = 842346, upload-time = "2025-11-17T12:59:28.092Z" }, + { url = "https://files.pythonhosted.org/packages/51/46/025f60fd3d51bf60606a0f8f0cd39c40068b9b5e4d249bca1682e4ff09c3/preshed-3.0.12-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:57159bcedca0cb4c99390f8a6e730f8659fdb663a5a3efcd9c4531e0f54b150e", size = 865504, upload-time = "2025-11-17T12:59:29.648Z" }, + { url = "https://files.pythonhosted.org/packages/88/b5/2e6ee5ab19b03e7983fc5e1850c812fb71dc178dd140d6aca3b45306bdf7/preshed-3.0.12-cp311-cp311-win_amd64.whl", hash = "sha256:8fe9cf1745e203e5aa58b8700436f78da1dcf0f0e2efb0054b467effd9d7d19d", size = 117736, upload-time = "2025-11-17T12:59:30.974Z" }, + { url = "https://files.pythonhosted.org/packages/1e/17/8a0a8f4b01e71b5fb7c5cd4c9fec04d7b852d42f1f9e096b01e7d2b16b17/preshed-3.0.12-cp311-cp311-win_arm64.whl", hash = "sha256:12d880f8786cb6deac34e99b8b07146fb92d22fbca0023208e03325f5944606b", size = 105127, upload-time = "2025-11-17T12:59:32.171Z" }, + { url = "https://files.pythonhosted.org/packages/4b/f7/ff3aca937eeaee19c52c45ddf92979546e52ed0686e58be4bc09c47e7d88/preshed-3.0.12-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2779861f5d69480493519ed123a622a13012d1182126779036b99d9d989bf7e9", size = 129958, upload-time = "2025-11-17T12:59:33.391Z" }, + { url = "https://files.pythonhosted.org/packages/80/24/fd654a9c0f5f3ed1a9b1d8a392f063ae9ca29ad0b462f0732ae0147f7cee/preshed-3.0.12-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffe1fd7d92f51ed34383e20d8b734780c814ca869cfdb7e07f2d31651f90cdf4", size = 124550, upload-time = "2025-11-17T12:59:34.688Z" }, + { url = "https://files.pythonhosted.org/packages/71/49/8271c7f680696f4b0880f44357d2a903d649cb9f6e60a1efc97a203104df/preshed-3.0.12-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:91893404858502cc4e856d338fef3d2a4a552135f79a1041c24eb919817c19db", size = 874987, upload-time = "2025-11-17T12:59:36.062Z" }, + { url = "https://files.pythonhosted.org/packages/a3/a5/ca200187ca1632f1e2c458b72f1bd100fa8b55deecd5d72e1e4ebf09e98c/preshed-3.0.12-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9e06e8f2ba52f183eb9817a616cdebe84a211bb859a2ffbc23f3295d0b189638", size = 866499, upload-time = "2025-11-17T12:59:37.586Z" }, + { url = "https://files.pythonhosted.org/packages/87/a1/943b61f850c44899910c21996cb542d0ef5931744c6d492fdfdd8457e693/preshed-3.0.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bbe8b8a2d4f9af14e8a39ecca524b9de6defc91d8abcc95eb28f42da1c23272c", size = 878064, upload-time = "2025-11-17T12:59:39.651Z" }, + { url = "https://files.pythonhosted.org/packages/3e/75/d7fff7f1fa3763619aa85d6ba70493a5d9c6e6ea7958a6e8c9d3e6e88bbe/preshed-3.0.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5d0aaac9c5862f5471fddd0c931dc64d3af2efc5fe3eb48b50765adb571243b9", size = 900540, upload-time = "2025-11-17T12:59:41.384Z" }, + { url = "https://files.pythonhosted.org/packages/e4/12/a2285b78bd097a1e53fb90a1743bc8ce0d35e5b65b6853f3b3c47da398ca/preshed-3.0.12-cp312-cp312-win_amd64.whl", hash = "sha256:0eb8d411afcb1e3b12a0602fb6a0e33140342a732a795251a0ce452aba401dc0", size = 118298, upload-time = "2025-11-17T12:59:42.65Z" }, + { url = "https://files.pythonhosted.org/packages/0b/34/4e8443fe99206a2fcfc63659969a8f8c8ab184836533594a519f3899b1ad/preshed-3.0.12-cp312-cp312-win_arm64.whl", hash = "sha256:dcd3d12903c9f720a39a5c5f1339f7f46e3ab71279fb7a39776768fb840b6077", size = 104746, upload-time = "2025-11-17T12:59:43.934Z" }, +] + +[[package]] +name = "propcache" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9e/da/e9fc233cf63743258bff22b3dfa7ea5baef7b5bc324af47a0ad89b8ffc6f/propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d", size = 46442, upload-time = "2025-10-08T19:49:02.291Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/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/8c/d4/4e2c9aaf7ac2242b9358f98dccd8f90f2605402f5afeff6c578682c2c491/propcache-0.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:60a8fda9644b7dfd5dece8c61d8a85e271cb958075bfc4e01083c148b61a7caf", size = 80208, upload-time = "2025-10-08T19:46:24.597Z" }, + { url = "https://files.pythonhosted.org/packages/c2/21/d7b68e911f9c8e18e4ae43bdbc1e1e9bbd971f8866eb81608947b6f585ff/propcache-0.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c30b53e7e6bda1d547cabb47c825f3843a0a1a42b0496087bb58d8fedf9f41b5", size = 45777, upload-time = "2025-10-08T19:46:25.733Z" }, + { url = "https://files.pythonhosted.org/packages/d3/1d/11605e99ac8ea9435651ee71ab4cb4bf03f0949586246476a25aadfec54a/propcache-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6918ecbd897443087a3b7cd978d56546a812517dcaaca51b49526720571fa93e", size = 47647, upload-time = "2025-10-08T19:46:27.304Z" }, + { url = "https://files.pythonhosted.org/packages/58/1a/3c62c127a8466c9c843bccb503d40a273e5cc69838805f322e2826509e0d/propcache-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3d902a36df4e5989763425a8ab9e98cd8ad5c52c823b34ee7ef307fd50582566", size = 214929, upload-time = "2025-10-08T19:46:28.62Z" }, + { url = "https://files.pythonhosted.org/packages/56/b9/8fa98f850960b367c4b8fe0592e7fc341daa7a9462e925228f10a60cf74f/propcache-0.4.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a9695397f85973bb40427dedddf70d8dc4a44b22f1650dd4af9eedf443d45165", size = 221778, upload-time = "2025-10-08T19:46:30.358Z" }, + { url = "https://files.pythonhosted.org/packages/46/a6/0ab4f660eb59649d14b3d3d65c439421cf2f87fe5dd68591cbe3c1e78a89/propcache-0.4.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2bb07ffd7eaad486576430c89f9b215f9e4be68c4866a96e97db9e97fead85dc", size = 228144, upload-time = "2025-10-08T19:46:32.607Z" }, + { url = "https://files.pythonhosted.org/packages/52/6a/57f43e054fb3d3a56ac9fc532bc684fc6169a26c75c353e65425b3e56eef/propcache-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd6f30fdcf9ae2a70abd34da54f18da086160e4d7d9251f81f3da0ff84fc5a48", size = 210030, upload-time = "2025-10-08T19:46:33.969Z" }, + { url = "https://files.pythonhosted.org/packages/40/e2/27e6feebb5f6b8408fa29f5efbb765cd54c153ac77314d27e457a3e993b7/propcache-0.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fc38cba02d1acba4e2869eef1a57a43dfbd3d49a59bf90dda7444ec2be6a5570", size = 208252, upload-time = "2025-10-08T19:46:35.309Z" }, + { url = "https://files.pythonhosted.org/packages/9e/f8/91c27b22ccda1dbc7967f921c42825564fa5336a01ecd72eb78a9f4f53c2/propcache-0.4.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:67fad6162281e80e882fb3ec355398cf72864a54069d060321f6cd0ade95fe85", size = 202064, upload-time = "2025-10-08T19:46:36.993Z" }, + { url = "https://files.pythonhosted.org/packages/f2/26/7f00bd6bd1adba5aafe5f4a66390f243acab58eab24ff1a08bebb2ef9d40/propcache-0.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f10207adf04d08bec185bae14d9606a1444715bc99180f9331c9c02093e1959e", size = 212429, upload-time = "2025-10-08T19:46:38.398Z" }, + { url = "https://files.pythonhosted.org/packages/84/89/fd108ba7815c1117ddca79c228f3f8a15fc82a73bca8b142eb5de13b2785/propcache-0.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e9b0d8d0845bbc4cfcdcbcdbf5086886bc8157aa963c31c777ceff7846c77757", size = 216727, upload-time = "2025-10-08T19:46:39.732Z" }, + { url = "https://files.pythonhosted.org/packages/79/37/3ec3f7e3173e73f1d600495d8b545b53802cbf35506e5732dd8578db3724/propcache-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:981333cb2f4c1896a12f4ab92a9cc8f09ea664e9b7dbdc4eff74627af3a11c0f", size = 205097, upload-time = "2025-10-08T19:46:41.025Z" }, + { url = "https://files.pythonhosted.org/packages/61/b0/b2631c19793f869d35f47d5a3a56fb19e9160d3c119f15ac7344fc3ccae7/propcache-0.4.1-cp311-cp311-win32.whl", hash = "sha256:f1d2f90aeec838a52f1c1a32fe9a619fefd5e411721a9117fbf82aea638fe8a1", size = 38084, upload-time = "2025-10-08T19:46:42.693Z" }, + { url = "https://files.pythonhosted.org/packages/f4/78/6cce448e2098e9f3bfc91bb877f06aa24b6ccace872e39c53b2f707c4648/propcache-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:364426a62660f3f699949ac8c621aad6977be7126c5807ce48c0aeb8e7333ea6", size = 41637, upload-time = "2025-10-08T19:46:43.778Z" }, + { url = "https://files.pythonhosted.org/packages/9c/e9/754f180cccd7f51a39913782c74717c581b9cc8177ad0e949f4d51812383/propcache-0.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:e53f3a38d3510c11953f3e6a33f205c6d1b001129f972805ca9b42fc308bc239", size = 38064, upload-time = "2025-10-08T19:46:44.872Z" }, + { url = "https://files.pythonhosted.org/packages/a2/0f/f17b1b2b221d5ca28b4b876e8bb046ac40466513960646bda8e1853cdfa2/propcache-0.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e153e9cd40cc8945138822807139367f256f89c6810c2634a4f6902b52d3b4e2", size = 80061, upload-time = "2025-10-08T19:46:46.075Z" }, + { url = "https://files.pythonhosted.org/packages/76/47/8ccf75935f51448ba9a16a71b783eb7ef6b9ee60f5d14c7f8a8a79fbeed7/propcache-0.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cd547953428f7abb73c5ad82cbb32109566204260d98e41e5dfdc682eb7f8403", size = 46037, upload-time = "2025-10-08T19:46:47.23Z" }, + { url = "https://files.pythonhosted.org/packages/0a/b6/5c9a0e42df4d00bfb4a3cbbe5cf9f54260300c88a0e9af1f47ca5ce17ac0/propcache-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f048da1b4f243fc44f205dfd320933a951b8d89e0afd4c7cacc762a8b9165207", size = 47324, upload-time = "2025-10-08T19:46:48.384Z" }, + { url = "https://files.pythonhosted.org/packages/9e/d3/6c7ee328b39a81ee877c962469f1e795f9db87f925251efeb0545e0020d0/propcache-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec17c65562a827bba85e3872ead335f95405ea1674860d96483a02f5c698fa72", size = 225505, upload-time = "2025-10-08T19:46:50.055Z" }, + { url = "https://files.pythonhosted.org/packages/01/5d/1c53f4563490b1d06a684742cc6076ef944bc6457df6051b7d1a877c057b/propcache-0.4.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:405aac25c6394ef275dee4c709be43745d36674b223ba4eb7144bf4d691b7367", size = 230242, upload-time = "2025-10-08T19:46:51.815Z" }, + { url = "https://files.pythonhosted.org/packages/20/e1/ce4620633b0e2422207c3cb774a0ee61cac13abc6217763a7b9e2e3f4a12/propcache-0.4.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0013cb6f8dde4b2a2f66903b8ba740bdfe378c943c4377a200551ceb27f379e4", size = 238474, upload-time = "2025-10-08T19:46:53.208Z" }, + { url = "https://files.pythonhosted.org/packages/46/4b/3aae6835b8e5f44ea6a68348ad90f78134047b503765087be2f9912140ea/propcache-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15932ab57837c3368b024473a525e25d316d8353016e7cc0e5ba9eb343fbb1cf", size = 221575, upload-time = "2025-10-08T19:46:54.511Z" }, + { url = "https://files.pythonhosted.org/packages/6e/a5/8a5e8678bcc9d3a1a15b9a29165640d64762d424a16af543f00629c87338/propcache-0.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:031dce78b9dc099f4c29785d9cf5577a3faf9ebf74ecbd3c856a7b92768c3df3", size = 216736, upload-time = "2025-10-08T19:46:56.212Z" }, + { url = "https://files.pythonhosted.org/packages/f1/63/b7b215eddeac83ca1c6b934f89d09a625aa9ee4ba158338854c87210cc36/propcache-0.4.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ab08df6c9a035bee56e31af99be621526bd237bea9f32def431c656b29e41778", size = 213019, upload-time = "2025-10-08T19:46:57.595Z" }, + { url = "https://files.pythonhosted.org/packages/57/74/f580099a58c8af587cac7ba19ee7cb418506342fbbe2d4a4401661cca886/propcache-0.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4d7af63f9f93fe593afbf104c21b3b15868efb2c21d07d8732c0c4287e66b6a6", size = 220376, upload-time = "2025-10-08T19:46:59.067Z" }, + { url = "https://files.pythonhosted.org/packages/c4/ee/542f1313aff7eaf19c2bb758c5d0560d2683dac001a1c96d0774af799843/propcache-0.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cfc27c945f422e8b5071b6e93169679e4eb5bf73bbcbf1ba3ae3a83d2f78ebd9", size = 226988, upload-time = "2025-10-08T19:47:00.544Z" }, + { url = "https://files.pythonhosted.org/packages/8f/18/9c6b015dd9c6930f6ce2229e1f02fb35298b847f2087ea2b436a5bfa7287/propcache-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:35c3277624a080cc6ec6f847cbbbb5b49affa3598c4535a0a4682a697aaa5c75", size = 215615, upload-time = "2025-10-08T19:47:01.968Z" }, + { url = "https://files.pythonhosted.org/packages/80/9e/e7b85720b98c45a45e1fca6a177024934dc9bc5f4d5dd04207f216fc33ed/propcache-0.4.1-cp312-cp312-win32.whl", hash = "sha256:671538c2262dadb5ba6395e26c1731e1d52534bfe9ae56d0b5573ce539266aa8", size = 38066, upload-time = "2025-10-08T19:47:03.503Z" }, + { url = "https://files.pythonhosted.org/packages/54/09/d19cff2a5aaac632ec8fc03737b223597b1e347416934c1b3a7df079784c/propcache-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:cb2d222e72399fcf5890d1d5cc1060857b9b236adff2792ff48ca2dfd46c81db", size = 41655, upload-time = "2025-10-08T19:47:04.973Z" }, + { url = "https://files.pythonhosted.org/packages/68/ab/6b5c191bb5de08036a8c697b265d4ca76148efb10fa162f14af14fb5f076/propcache-0.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:204483131fb222bdaaeeea9f9e6c6ed0cac32731f75dfc1d4a567fc1926477c1", size = 37789, upload-time = "2025-10-08T19:47:06.077Z" }, + { url = "https://files.pythonhosted.org/packages/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 = "5.29.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/57/394a763c103e0edf87f0938dafcd918d53b4c011dfc5c8ae80f3b0452dbb/protobuf-5.29.6.tar.gz", hash = "sha256:da9ee6a5424b6b30fd5e45c5ea663aef540ca95f9ad99d1e887e819cdf9b8723", size = 425623, upload-time = "2026-02-04T22:54:40.584Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d4/88/9ee58ff7863c479d6f8346686d4636dd4c415b0cbeed7a6a7d0617639c2a/protobuf-5.29.6-cp310-abi3-win32.whl", hash = "sha256:62e8a3114992c7c647bce37dcc93647575fc52d50e48de30c6fcb28a6a291eb1", size = 423357, upload-time = "2026-02-04T22:54:25.805Z" }, + { url = "https://files.pythonhosted.org/packages/1c/66/2dc736a4d576847134fb6d80bd995c569b13cdc7b815d669050bf0ce2d2c/protobuf-5.29.6-cp310-abi3-win_amd64.whl", hash = "sha256:7e6ad413275be172f67fdee0f43484b6de5a904cc1c3ea9804cb6fe2ff366eda", size = 435175, upload-time = "2026-02-04T22:54:28.592Z" }, + { url = "https://files.pythonhosted.org/packages/06/db/49b05966fd208ae3f44dcd33837b6243b4915c57561d730a43f881f24dea/protobuf-5.29.6-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:b5a169e664b4057183a34bdc424540e86eea47560f3c123a0d64de4e137f9269", size = 418619, upload-time = "2026-02-04T22:54:30.266Z" }, + { url = "https://files.pythonhosted.org/packages/b7/d7/48cbf6b0c3c39761e47a99cb483405f0fde2be22cf00d71ef316ce52b458/protobuf-5.29.6-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:a8866b2cff111f0f863c1b3b9e7572dc7eaea23a7fae27f6fc613304046483e6", size = 320284, upload-time = "2026-02-04T22:54:31.782Z" }, + { url = "https://files.pythonhosted.org/packages/e3/dd/cadd6ec43069247d91f6345fa7a0d2858bef6af366dbd7ba8f05d2c77d3b/protobuf-5.29.6-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:e3387f44798ac1106af0233c04fb8abf543772ff241169946f698b3a9a3d3ab9", size = 320478, upload-time = "2026-02-04T22:54:32.909Z" }, + { url = "https://files.pythonhosted.org/packages/5a/cb/e3065b447186cb70aa65acc70c86baf482d82bf75625bf5a2c4f6919c6a3/protobuf-5.29.6-py3-none-any.whl", hash = "sha256:6b9edb641441b2da9fa8f428760fc136a49cf97a52076010cf22a2ff73438a86", size = 173126, upload-time = "2026-02-04T22:54:39.462Z" }, +] + +[[package]] +name = "psutil" +version = "7.1.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e1/88/bdd0a41e5857d5d703287598cbf08dad90aed56774ea52ae071bae9071b6/psutil-7.1.3.tar.gz", hash = "sha256:6c86281738d77335af7aec228328e944b30930899ea760ecf33a4dba66be5e74", size = 489059, upload-time = "2025-11-02T12:25:54.619Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/94/46b9154a800253e7ecff5aaacdf8ebf43db99de4a2dfa18575b02548654e/psutil-7.1.3-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2bdbcd0e58ca14996a42adf3621a6244f1bb2e2e528886959c72cf1e326677ab", size = 238359, upload-time = "2025-11-02T12:26:25.284Z" }, + { url = "https://files.pythonhosted.org/packages/68/3a/9f93cff5c025029a36d9a92fef47220ab4692ee7f2be0fba9f92813d0cb8/psutil-7.1.3-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:bc31fa00f1fbc3c3802141eede66f3a2d51d89716a194bf2cd6fc68310a19880", size = 239171, upload-time = "2025-11-02T12:26:27.23Z" }, + { url = "https://files.pythonhosted.org/packages/ce/b1/5f49af514f76431ba4eea935b8ad3725cdeb397e9245ab919dbc1d1dc20f/psutil-7.1.3-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3bb428f9f05c1225a558f53e30ccbad9930b11c3fc206836242de1091d3e7dd3", size = 263261, upload-time = "2025-11-02T12:26:29.48Z" }, + { url = "https://files.pythonhosted.org/packages/e0/95/992c8816a74016eb095e73585d747e0a8ea21a061ed3689474fabb29a395/psutil-7.1.3-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:56d974e02ca2c8eb4812c3f76c30e28836fffc311d55d979f1465c1feeb2b68b", size = 264635, upload-time = "2025-11-02T12:26:31.74Z" }, + { url = "https://files.pythonhosted.org/packages/55/4c/c3ed1a622b6ae2fd3c945a366e64eb35247a31e4db16cf5095e269e8eb3c/psutil-7.1.3-cp37-abi3-win_amd64.whl", hash = "sha256:f39c2c19fe824b47484b96f9692932248a54c43799a84282cfe58d05a6449efd", size = 247633, upload-time = "2025-11-02T12:26:33.887Z" }, + { url = "https://files.pythonhosted.org/packages/c9/ad/33b2ccec09bf96c2b2ef3f9a6f66baac8253d7565d8839e024a6b905d45d/psutil-7.1.3-cp37-abi3-win_arm64.whl", hash = "sha256:bd0d69cee829226a761e92f28140bec9a5ee9d5b4fb4b0cc589068dbfff559b1", size = 244608, upload-time = "2025-11-02T12:26:36.136Z" }, +] + +[[package]] +name = "py-vncorenlp" +version = "0.1.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyjnius" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/48/bb/6aee7ee062eb4613560e23a6f8488c0b92d72d971d2f298b48caf63cf5fc/py_vncorenlp-0.1.4.tar.gz", hash = "sha256:9049209e838b0fa1fcefa4b24695afc0e51eb4b38ec4e1279402c20c9a0680e8", size = 3907, upload-time = "2023-02-13T02:37:10.149Z" } + +[[package]] +name = "pycocotools" +version = "2.0.10" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/35/a6/694fd661f0feb5e91f7049a202ea12de312ca9010c33bd9d9f0c63046c01/pycocotools-2.0.10.tar.gz", hash = "sha256:7a47609cdefc95e5e151313c7d93a61cf06e15d42c7ba99b601e3bc0f9ece2e1", size = 25389, upload-time = "2025-06-04T23:37:47.879Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/f8/24082061458ad62df7e2714a631cc047eddfe752970a2e4a7e7977d96905/pycocotools-2.0.10-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:94d558e6a4b92620dad1684b74b6c1404e20d5ed3b4f3aed64ad817d5dd46c72", size = 152202, upload-time = "2025-06-04T23:36:50.026Z" }, + { url = "https://files.pythonhosted.org/packages/fe/45/65819da7579e9018506ed3b5401146a394e89eee84f57592174962f0fba2/pycocotools-2.0.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4d61959f505f1333afd1666ece1a9f8dad318de160c56c7d03f22d7b5556478", size = 445796, upload-time = "2025-06-04T23:36:52.057Z" }, + { url = "https://files.pythonhosted.org/packages/61/d7/32996d713921c504875a4cebf241c182aa37e58daab5c3c4737f539ac0d4/pycocotools-2.0.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0bb54826c5d3b651597ec15ae5f4226b727159ec7798af81aa3895f734518993", size = 455015, upload-time = "2025-06-04T23:36:53.93Z" }, + { url = "https://files.pythonhosted.org/packages/fe/5f/91ad9e46ec6709d24a9ed8ac3969f6a550715c08b22f85bc045d1395fdf6/pycocotools-2.0.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9d3b4d0aa38c76153ec244f17939bbc65d24b6a119eb99184f7f636421ef0d8a", size = 464739, upload-time = "2025-06-04T23:36:55.751Z" }, + { url = "https://files.pythonhosted.org/packages/40/e3/9684edbd996a35d8da7c38c1dfc151d6e1bcf66bd32de6fb88f6d2f2bcf5/pycocotools-2.0.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:714dda1fccc3a9a1f10893530df6e927678daf6c49bc8a932d7ec2042e9a11f2", size = 481572, upload-time = "2025-06-04T23:36:57.374Z" }, + { url = "https://files.pythonhosted.org/packages/4e/84/1832144e8effe700660489d6e2a7687c99d14c3ea29fa0142dac0e7322d6/pycocotools-2.0.10-cp310-cp310-win_amd64.whl", hash = "sha256:8b4f26d44dde3e0b1e3df3ddcc7e27560e52dfe53db708c26af22a57e8ea3d47", size = 80166, upload-time = "2025-06-04T23:36:59.275Z" }, + { url = "https://files.pythonhosted.org/packages/03/bf/ea288c16d2d2e4da740545f30f7ebf58f2343bcf5e0a7f3e3aef582a116c/pycocotools-2.0.10-cp310-cp310-win_arm64.whl", hash = "sha256:16836530552d6ce5e7f1cbcdfe6ead94c0cee71d61bfa3e3c832aef57d21c027", size = 69633, upload-time = "2025-06-04T23:37:00.527Z" }, + { url = "https://files.pythonhosted.org/packages/ee/36/aebbbddd9c659f1fc9d78daeaf6e39860813bb014b0de873073361ad40f1/pycocotools-2.0.10-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:68846da0ee3ea82d71bcbd99ed28271633a67a899cfbacd2ef309b2e455524b2", size = 155033, upload-time = "2025-06-04T23:37:01.835Z" }, + { url = "https://files.pythonhosted.org/packages/57/c2/e4c96950604c709fbd71c49828968fadd9d8ca8cf74f52be4cd4b2ff9300/pycocotools-2.0.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20831839a771d4bc60a814e7b54a92d9a45a773dee47959d30888d00066059c3", size = 470328, upload-time = "2025-06-04T23:37:03.675Z" }, + { url = "https://files.pythonhosted.org/packages/a7/ec/7827cd9ce6e80f739fab0163ecb3765df54af744a9bab64b0058bdce47ef/pycocotools-2.0.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1760c10459dfb4229e7436ae380228428efb0115bbe332a51b72d07fa085d8c0", size = 477331, upload-time = "2025-06-04T23:37:05.703Z" }, + { url = "https://files.pythonhosted.org/packages/81/74/33ce685ae1cd6312b2526f701e43dfeb73d1c860878b72a30ac1cc322536/pycocotools-2.0.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5146bc881f380e8fb493e49216083298e4a06f778841f8b9b1d45b21e211d0e4", size = 489735, upload-time = "2025-06-04T23:37:08.488Z" }, + { url = "https://files.pythonhosted.org/packages/17/79/0e02ce700ff9c9fd30e57a84add42bd6fc033e743b76870ef68215d3f3f4/pycocotools-2.0.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:23f7d0c551d4c31cab629ce177186db9562f10414320add5267707a84cf6cdfa", size = 507779, upload-time = "2025-06-04T23:37:10.159Z" }, + { url = "https://files.pythonhosted.org/packages/d5/12/00fac39ad26f762c50e5428cc8b3c83de28c5d64b5b858181583522a4e28/pycocotools-2.0.10-cp311-cp311-win_amd64.whl", hash = "sha256:03c3aacec2a6aa5171016303a539d07a7b22a34557456eadf0eb40853bdd813e", size = 80808, upload-time = "2025-06-04T23:37:11.865Z" }, + { url = "https://files.pythonhosted.org/packages/3d/cd/50970a64365f013151086d54d60b40369cf612f117d72cd9d6bd2966932c/pycocotools-2.0.10-cp311-cp311-win_arm64.whl", hash = "sha256:1f942352b1ab11b9732443ab832cbe5836441f4ec30e1f61b44e1421dbb0a0f5", size = 69566, upload-time = "2025-06-04T23:37:13.067Z" }, + { url = "https://files.pythonhosted.org/packages/d7/b4/3b87dce90fc81b8283b2b0e32b22642939e25f3a949581cb6777f5eebb12/pycocotools-2.0.10-cp312-abi3-macosx_10_13_universal2.whl", hash = "sha256:e1359f556986c8c4ac996bf8e473ff891d87630491357aaabd12601687af5edb", size = 142896, upload-time = "2025-06-04T23:37:14.748Z" }, + { url = "https://files.pythonhosted.org/packages/29/d5/b17bb67722432a191cb86121cda33cd8edb4d5b15beda43bc97a7d5ae404/pycocotools-2.0.10-cp312-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:075788c90bfa6a8989d628932854f3e32c25dac3c1bf7c1183cefad29aee16c8", size = 390111, upload-time = "2025-06-04T23:37:16.588Z" }, + { url = "https://files.pythonhosted.org/packages/49/80/912b4c60f94e747dd2c3adbda5d4a4edc1d735fbfa0d91ab2eb231decb5d/pycocotools-2.0.10-cp312-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4539d8b29230de042f574012edd0b5227528da083c4f12bbd6488567aabd3920", size = 397099, upload-time = "2025-06-04T23:37:18.105Z" }, + { url = "https://files.pythonhosted.org/packages/df/d7/b3c2f731252a096bbae1a47cb1bbeab4560620a82585d40cce67eca5f043/pycocotools-2.0.10-cp312-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:da7b339624d0f78aa5bdc1c86a53f2dcb36ae7e10ab5fe45ba69878bb7837c7a", size = 396111, upload-time = "2025-06-04T23:37:20.642Z" }, + { url = "https://files.pythonhosted.org/packages/2c/6f/2eceba57245bfc86174263e12716cbe91b329a3677fbeff246148ce6a664/pycocotools-2.0.10-cp312-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ffdbf8810f27b32c5c5c85d9cd65e8e066852fef9775e58a7b23abdffeaf8252", size = 416393, upload-time = "2025-06-04T23:37:22.287Z" }, + { url = "https://files.pythonhosted.org/packages/e1/31/d87f781759b2ad177dd6d41c5fe0ce154f14fc8b384e9b80cd21a157395b/pycocotools-2.0.10-cp312-abi3-win_amd64.whl", hash = "sha256:998a88f90bb663548e767470181175343d406b6673b8b9ef5bdbb3a6d3eb3b11", size = 76824, upload-time = "2025-06-04T23:37:23.744Z" }, + { url = "https://files.pythonhosted.org/packages/27/13/7674d61658b58b8310e3de1270bce18f92a6ee8136e54a7e5696d6f72fd4/pycocotools-2.0.10-cp312-abi3-win_arm64.whl", hash = "sha256:76cd86a80171f8f7da3250be0e40d75084f1f1505d376ae0d08ed0be1ba8a90d", size = 64753, upload-time = "2025-06-04T23:37:25.202Z" }, +] + +[[package]] +name = "pydantic" +version = "2.12.4" +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/96/ad/a17bc283d7d81837c061c49e3eaa27a45991759a1b7eae1031921c6bd924/pydantic-2.12.4.tar.gz", hash = "sha256:0f8cb9555000a4b5b617f66bfd2566264c4984b27589d3b845685983e8ea85ac", size = 821038, upload-time = "2025-11-05T10:50:08.59Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/2f/e68750da9b04856e2a7ec56fc6f034a5a79775e9b9a81882252789873798/pydantic-2.12.4-py3-none-any.whl", hash = "sha256:92d3d202a745d46f9be6df459ac5a064fdaa3c1c4cd8adcfa332ccf3c05f871e", size = 463400, upload-time = "2025-11-05T10:50:06.732Z" }, +] + +[[package]] +name = "pydantic-core" +version = "2.41.5" +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/e8/72/74a989dd9f2084b3d9530b0915fdda64ac48831c30dbf7c72a41a5232db8/pydantic_core-2.41.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6", size = 2105873, upload-time = "2025-11-04T13:39:31.373Z" }, + { url = "https://files.pythonhosted.org/packages/12/44/37e403fd9455708b3b942949e1d7febc02167662bf1a7da5b78ee1ea2842/pydantic_core-2.41.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b", size = 1899826, upload-time = "2025-11-04T13:39:32.897Z" }, + { url = "https://files.pythonhosted.org/packages/33/7f/1d5cab3ccf44c1935a359d51a8a2a9e1a654b744b5e7f80d41b88d501eec/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a", size = 1917869, upload-time = "2025-11-04T13:39:34.469Z" }, + { url = "https://files.pythonhosted.org/packages/6e/6a/30d94a9674a7fe4f4744052ed6c5e083424510be1e93da5bc47569d11810/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8", size = 2063890, upload-time = "2025-11-04T13:39:36.053Z" }, + { url = "https://files.pythonhosted.org/packages/50/be/76e5d46203fcb2750e542f32e6c371ffa9b8ad17364cf94bb0818dbfb50c/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e", size = 2229740, upload-time = "2025-11-04T13:39:37.753Z" }, + { url = "https://files.pythonhosted.org/packages/d3/ee/fed784df0144793489f87db310a6bbf8118d7b630ed07aa180d6067e653a/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1", size = 2350021, upload-time = "2025-11-04T13:39:40.94Z" }, + { url = "https://files.pythonhosted.org/packages/c8/be/8fed28dd0a180dca19e72c233cbf58efa36df055e5b9d90d64fd1740b828/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b", size = 2066378, upload-time = "2025-11-04T13:39:42.523Z" }, + { url = "https://files.pythonhosted.org/packages/b0/3b/698cf8ae1d536a010e05121b4958b1257f0b5522085e335360e53a6b1c8b/pydantic_core-2.41.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b", size = 2175761, upload-time = "2025-11-04T13:39:44.553Z" }, + { url = "https://files.pythonhosted.org/packages/b8/ba/15d537423939553116dea94ce02f9c31be0fa9d0b806d427e0308ec17145/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284", size = 2146303, upload-time = "2025-11-04T13:39:46.238Z" }, + { url = "https://files.pythonhosted.org/packages/58/7f/0de669bf37d206723795f9c90c82966726a2ab06c336deba4735b55af431/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594", size = 2340355, upload-time = "2025-11-04T13:39:48.002Z" }, + { url = "https://files.pythonhosted.org/packages/e5/de/e7482c435b83d7e3c3ee5ee4451f6e8973cff0eb6007d2872ce6383f6398/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e", size = 2319875, upload-time = "2025-11-04T13:39:49.705Z" }, + { url = "https://files.pythonhosted.org/packages/fe/e6/8c9e81bb6dd7560e33b9053351c29f30c8194b72f2d6932888581f503482/pydantic_core-2.41.5-cp311-cp311-win32.whl", hash = "sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b", size = 1987549, upload-time = "2025-11-04T13:39:51.842Z" }, + { url = "https://files.pythonhosted.org/packages/11/66/f14d1d978ea94d1bc21fc98fcf570f9542fe55bfcc40269d4e1a21c19bf7/pydantic_core-2.41.5-cp311-cp311-win_amd64.whl", hash = "sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe", size = 2011305, upload-time = "2025-11-04T13:39:53.485Z" }, + { url = "https://files.pythonhosted.org/packages/56/d8/0e271434e8efd03186c5386671328154ee349ff0354d83c74f5caaf096ed/pydantic_core-2.41.5-cp311-cp311-win_arm64.whl", hash = "sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f", size = 1972902, upload-time = "2025-11-04T13:39:56.488Z" }, + { url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" }, + { url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" }, + { url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" }, + { url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578, upload-time = "2025-11-04T13:40:04.401Z" }, + { url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504, upload-time = "2025-11-04T13:40:06.072Z" }, + { url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816, upload-time = "2025-11-04T13:40:07.835Z" }, + { url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366, upload-time = "2025-11-04T13:40:09.804Z" }, + { url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698, upload-time = "2025-11-04T13:40:12.004Z" }, + { url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603, upload-time = "2025-11-04T13:40:13.868Z" }, + { url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591, upload-time = "2025-11-04T13:40:15.672Z" }, + { url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068, upload-time = "2025-11-04T13:40:17.532Z" }, + { url = "https://files.pythonhosted.org/packages/49/3b/774f2b5cd4192d5ab75870ce4381fd89cf218af999515baf07e7206753f0/pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", size = 1985908, upload-time = "2025-11-04T13:40:19.309Z" }, + { url = "https://files.pythonhosted.org/packages/86/45/00173a033c801cacf67c190fef088789394feaf88a98a7035b0e40d53dc9/pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", size = 2020145, upload-time = "2025-11-04T13:40:21.548Z" }, + { url = "https://files.pythonhosted.org/packages/f9/22/91fbc821fa6d261b376a3f73809f907cec5ca6025642c463d3488aad22fb/pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", size = 1976179, upload-time = "2025-11-04T13:40:23.393Z" }, + { 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, upload-time = "2025-11-04T13:42:39.557Z" }, + { 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, upload-time = "2025-11-04T13:42:42.169Z" }, + { 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, upload-time = "2025-11-04T13:42:44.564Z" }, + { 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, upload-time = "2025-11-04T13:42:47.156Z" }, + { 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, upload-time = "2025-11-04T13:42:49.689Z" }, + { 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, upload-time = "2025-11-04T13:42:52.215Z" }, + { 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, upload-time = "2025-11-04T13:42:56.483Z" }, + { 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, upload-time = "2025-11-04T13:42:59.471Z" }, + { 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" }, + { url = "https://files.pythonhosted.org/packages/5f/9b/1b3f0e9f9305839d7e84912f9e8bfbd191ed1b1ef48083609f0dabde978c/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26", size = 2101980, upload-time = "2025-11-04T13:43:25.97Z" }, + { url = "https://files.pythonhosted.org/packages/a4/ed/d71fefcb4263df0da6a85b5d8a7508360f2f2e9b3bf5814be9c8bccdccc1/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808", size = 1923865, upload-time = "2025-11-04T13:43:28.763Z" }, + { url = "https://files.pythonhosted.org/packages/ce/3a/626b38db460d675f873e4444b4bb030453bbe7b4ba55df821d026a0493c4/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc", size = 2134256, upload-time = "2025-11-04T13:43:31.71Z" }, + { url = "https://files.pythonhosted.org/packages/83/d9/8412d7f06f616bbc053d30cb4e5f76786af3221462ad5eee1f202021eb4e/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1", size = 2174762, upload-time = "2025-11-04T13:43:34.744Z" }, + { url = "https://files.pythonhosted.org/packages/55/4c/162d906b8e3ba3a99354e20faa1b49a85206c47de97a639510a0e673f5da/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84", size = 2143141, upload-time = "2025-11-04T13:43:37.701Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f2/f11dd73284122713f5f89fc940f370d035fa8e1e078d446b3313955157fe/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770", size = 2330317, upload-time = "2025-11-04T13:43:40.406Z" }, + { url = "https://files.pythonhosted.org/packages/88/9d/b06ca6acfe4abb296110fb1273a4d848a0bfb2ff65f3ee92127b3244e16b/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f", size = 2316992, upload-time = "2025-11-04T13:43:43.602Z" }, + { url = "https://files.pythonhosted.org/packages/36/c7/cfc8e811f061c841d7990b0201912c3556bfeb99cdcb7ed24adc8d6f8704/pydantic_core-2.41.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51", size = 2145302, upload-time = "2025-11-04T13:43:46.64Z" }, +] + +[[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" } +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" }, +] + +[[package]] +name = "pyjnius" +version = "1.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/04/a2/22bcf51dd6628e1ab9932a5e3e3e0b062ca4e2ef80e1bc9f7a3cc2748c57/pyjnius-1.7.0.tar.gz", hash = "sha256:9f81708484b0a84ead3eb0ba84e53ac579e4c43ca10c746f9898a9f3dd50f54d", size = 66363, upload-time = "2025-09-08T19:33:14.675Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/a5/b3a6d9e92cc02400a6586f09c5ae0cdc3a9c7999bc79c6126345c972c6d2/pyjnius-1.7.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:895543816feb0c3a6e04abebe93c2fa331413efd9d8e94a2454cfcf16145cc6e", size = 447601, upload-time = "2025-09-08T19:32:03.565Z" }, + { url = "https://files.pythonhosted.org/packages/45/ec/b0a8fcaa4ef8a28583791914712cf8aae0cc93ff4d0218f14256d923a35b/pyjnius-1.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3e6a3a936cc1763f4f04dda7d3fe91e403f8fe6a11c4b35071f4864f381f88da", size = 239590, upload-time = "2025-09-08T19:32:05.427Z" }, + { url = "https://files.pythonhosted.org/packages/82/f5/45035802b48b35a463f023365a37bede87ca7b3fc4a48b7ec0c155101f40/pyjnius-1.7.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:438a64de79710c2d6ef7683a088c4a4e62e56c1b07fffa01f5fa7cb4db456b46", size = 1511958, upload-time = "2025-09-08T19:32:07.174Z" }, + { url = "https://files.pythonhosted.org/packages/4d/96/d3597b47a8e7dcc73f0f1f3dfd89cd25a78586b90cd2760c7fd89d5edac5/pyjnius-1.7.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95049134096a0431a9fc91a350f36da3155946e30b205573144f5e6418e692d1", size = 1557426, upload-time = "2025-09-08T19:32:08.523Z" }, + { url = "https://files.pythonhosted.org/packages/87/c3/b4c81b50385c9021842205d7b80286d9a0b2e8348235a5e2a76ea8115bd8/pyjnius-1.7.0-cp310-cp310-win32.whl", hash = "sha256:80870ed845206c4c71f4dd20403cdf534e0e43b7f700d786df5d62aa6bf7732a", size = 175110, upload-time = "2025-09-08T19:32:10.248Z" }, + { url = "https://files.pythonhosted.org/packages/5f/bf/4e51fcfd9c00458ed7d4fb5bfe2c2acd98fd6e8b26b4b62cb81eee29a774/pyjnius-1.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:997c1fee5d5f607a54b58bcf60c77d60b9ed33101a05b312a500c4dcf77da8da", size = 212630, upload-time = "2025-09-08T19:32:11.808Z" }, + { url = "https://files.pythonhosted.org/packages/9e/c0/92c2eddeb54f439ff63769bc88151d754457c478e2532d95c1ca7fa7ee77/pyjnius-1.7.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0b4fe8c65cef7d49c8b649c56d63dfb95e34817300a2f5b3ab3457631a69e3bf", size = 451457, upload-time = "2025-09-08T19:32:13.535Z" }, + { url = "https://files.pythonhosted.org/packages/b9/2c/ca2adf3a772c7cb29b41a48087477602920afb6917a1c144f2137cd5c091/pyjnius-1.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a82fac23fbcd83c2eb1e9f81ee5075eb878f17e4c46a1169e75401bf03971347", size = 240913, upload-time = "2025-09-08T19:32:14.717Z" }, + { url = "https://files.pythonhosted.org/packages/24/cd/1258b96966bc4eb814a85033c61981d5f07a3363ae6e94e7a36d5658fbea/pyjnius-1.7.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a07f460d363903afe719ef2607bf30f1d7d0241aa4e83ec1fd7a7979268edf20", size = 1615333, upload-time = "2025-09-08T19:32:16.484Z" }, + { url = "https://files.pythonhosted.org/packages/6f/0a/8de1a82b2c4722fd7471a08e9c2b1c6fa2d5fa783305afc8e915ba8c8f08/pyjnius-1.7.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:73f053085ce9ce04b55c09a9b5d756e40783f30b3ad47008960d23241cf344d7", size = 1657139, upload-time = "2025-09-08T19:32:17.821Z" }, + { url = "https://files.pythonhosted.org/packages/b8/28/30dd2b95542bacaa244f97d89033fa1ccc650c7d37304dbde436f210862f/pyjnius-1.7.0-cp311-cp311-win32.whl", hash = "sha256:bb7fe15111d8586b415bbdbd4681852e55895f0485a0ff630af654290606f21f", size = 173415, upload-time = "2025-09-08T19:32:18.935Z" }, + { url = "https://files.pythonhosted.org/packages/77/39/876f7ac7f184cd0e7308dc2d391825ae77826f709d114c4293e9fb411be4/pyjnius-1.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:756322d6d197fd93a1b14fc8b90237bb4d3bba7a8d661dfcf8a6d872dfb25984", size = 212844, upload-time = "2025-09-08T19:32:20.467Z" }, + { url = "https://files.pythonhosted.org/packages/ef/6a/07c9e26d02a6151a1ab70994550ed715c10e4ec0c6c6feaa80939f0a0a1f/pyjnius-1.7.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:33897a07d79074b8d43a111237fa88e06d18807cfb3e9e2555c29b5eab26ebe3", size = 456806, upload-time = "2025-09-08T19:32:22.42Z" }, + { url = "https://files.pythonhosted.org/packages/14/e5/13ecc419d76f8e978632275729a9b0f271c557fea9d244dc908e5f74623d/pyjnius-1.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2fb1feb02a210b4251a0df49bb3210fa44f38a240e703caa9a4ab90df9d69c08", size = 243394, upload-time = "2025-09-08T19:32:23.952Z" }, + { url = "https://files.pythonhosted.org/packages/78/82/f8304afc8dcfcfba73d5c4baf6486f835867efc98c9bdc4ad7f68f26e708/pyjnius-1.7.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef19f7f0a841e75d8fb046ac0bf41439c8fec92e89f865ca657155ab1e1524de", size = 1562298, upload-time = "2025-09-08T19:32:25.344Z" }, + { url = "https://files.pythonhosted.org/packages/e3/72/61560b71ead2c3e55bb4367db332651b97b6a8bdfd9fb1d37579b8e0399c/pyjnius-1.7.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b911f35bea871b5181d17350d441776cb434cdf61b59893242d21ca6eb55859", size = 1610463, upload-time = "2025-09-08T19:32:27.605Z" }, + { url = "https://files.pythonhosted.org/packages/dd/53/ff426369f6d03cd09ebbfcb5eef1cc180b445185795d7955bebb88f82170/pyjnius-1.7.0-cp312-cp312-win32.whl", hash = "sha256:b708f65edd962a8cb4286acfd77b89ed2d0fa25e0e4a6abc51add23ddf87a638", size = 174536, upload-time = "2025-09-08T19:32:29.22Z" }, + { url = "https://files.pythonhosted.org/packages/5f/0d/b52b2aee0e52a24e255784820546c3c2ff609fb4eabe53f1efe0170398c1/pyjnius-1.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:a618a59951e9345c73dda37836cfc1c41ae46c8a3c25e6d9c81217c909b86811", size = 209194, upload-time = "2025-09-08T19:32:31.086Z" }, +] + +[[package]] +name = "pyparsing" +version = "3.2.5" +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, upload-time = "2025-09-21T04:11:06.277Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/5e/1aa9a93198c6b64513c9d7752de7422c06402de6600a8767da1524f9570b/pyparsing-3.2.5-py3-none-any.whl", hash = "sha256:e38a4f02064cf41fe6593d328d0512495ad1f3d8a91c4f73fc401b3079a59a5e", size = 113890, upload-time = "2025-09-21T04:11:04.117Z" }, +] + +[[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" } +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" }, +] + +[[package]] +name = "pytest" +version = "9.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/07/56/f013048ac4bc4c1d9be45afd4ab209ea62822fb1598f40687e6bf45dcea4/pytest-9.0.1.tar.gz", hash = "sha256:3e9c069ea73583e255c3b21cf46b8d3c56f6e3a1a8f6da94ccb0fcf57b9d73c8", size = 1564125, upload-time = "2025-11-12T13:05:09.333Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/8b/6300fb80f858cda1c51ffa17075df5d846757081d11ab4aa35cef9e6258b/pytest-9.0.1-py3-none-any.whl", hash = "sha256:67be0030d194df2dfa7b556f2e56fb3c3315bd5c8822c6951162b92b32ce7dad", size = 373668, upload-time = "2025-11-12T13:05:07.379Z" }, +] + +[[package]] +name = "pytest-order" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1d/66/02ae17461b14a52ce5a29ae2900156b9110d1de34721ccc16ccd79419876/pytest_order-1.3.0.tar.gz", hash = "sha256:51608fec3d3ee9c0adaea94daa124a5c4c1d2bb99b00269f098f414307f23dde", size = 47544, upload-time = "2024-08-22T12:29:54.512Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1b/73/59b038d1aafca89f8e9936eaa8ffa6bb6138d00459d13a32ce070be4f280/pytest_order-1.3.0-py3-none-any.whl", hash = "sha256:2cd562a21380345dd8d5774aa5fd38b7849b6ee7397ca5f6999bbe6e89f07f6e", size = 14609, upload-time = "2024-08-22T12:29:53.156Z" }, +] + +[[package]] +name = "pytest-timeout" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ac/82/4c9ecabab13363e72d880f2fb504c5f750433b2b6f16e99f4ec21ada284c/pytest_timeout-2.4.0.tar.gz", hash = "sha256:7e68e90b01f9eff71332b25001f85c75495fc4e3a836701876183c4bcfd0540a", size = 17973, upload-time = "2025-05-05T19:44:34.99Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl", hash = "sha256:c42667e5cdadb151aeb5b26d114aff6bdf5a907f176a007a30b940d3d865b5c2", size = 14382, upload-time = "2025-05-05T19:44:33.502Z" }, +] + +[[package]] +name = "python-crfsuite" +version = "0.9.11" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ea/17/9c64a2486de27fce7570c366eb723fd7a39dd7845119ee8fdd5051023671/python_crfsuite-0.9.11.tar.gz", hash = "sha256:6eff965ca70567396d822c9a35ea74b0f7edb27d9471524997bdabe7a6da5f5a", size = 477721, upload-time = "2024-10-02T13:01:16.646Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/74/6335c271196db419c77ddfb19c04abe40fee45921faca96c150bfc51f176/python_crfsuite-0.9.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2f5ed569517e7b1fa3d32cf5d5cbe2fb6c85486195bf5cad03d52072fef7aa8a", size = 319014, upload-time = "2024-10-02T12:59:52.622Z" }, + { url = "https://files.pythonhosted.org/packages/38/32/b2986996b4be2edeb13b62b5115aa042250f5da87edd3f8db4d71d6d1457/python_crfsuite-0.9.11-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aed10ee4334c99173940e88318d312a4f9e70ba653b8ac0e6f3ef816431af811", size = 1183446, upload-time = "2024-10-02T12:59:54.601Z" }, + { url = "https://files.pythonhosted.org/packages/5f/46/bea59e70e382aeeb1cd47e04868ce3bcec622968af4b01b24b272d8050ad/python_crfsuite-0.9.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4fd8cc52f853436bbed580ad6c17e37c3657466fdfa28ddc55efcbba28b92cdf", size = 1215316, upload-time = "2024-10-02T12:59:55.891Z" }, + { url = "https://files.pythonhosted.org/packages/4d/ba/f4c169ce26689ddf4eb92effb4b15dafbb52622302f15a67492c680f3e0c/python_crfsuite-0.9.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:182fad0415697d5acbe18364333f8255016c8609d570cba78c20d8d71a392f90", size = 1232601, upload-time = "2024-10-02T12:59:57.495Z" }, + { url = "https://files.pythonhosted.org/packages/54/cb/b38bd20df2274570275dc97515f7cf8475ef8ffc50bc6d9dffaafd9a8600/python_crfsuite-0.9.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:05cd988aaa7ac87a54d4bd1d756455f6e3b078f07b4fcbda3bccfd91a784dd20", size = 2103655, upload-time = "2024-10-02T12:59:59.181Z" }, + { url = "https://files.pythonhosted.org/packages/fa/d0/16f0d8bcc284afb197a22cc923c5cbd219b12ee1482c3ebb237bee48f957/python_crfsuite-0.9.11-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:2dead957809b92b7f0fc4c03fc70af9cbcaf35518ff1fd3a3fe2862dd0bb52fa", size = 2250127, upload-time = "2024-10-02T13:00:00.615Z" }, + { url = "https://files.pythonhosted.org/packages/51/0e/8ab90bfc316ff326588f6bbbb2eef1776ca11e91e22ab4eaced483b8f774/python_crfsuite-0.9.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:66f24e5281b8a10091c3a9eef5a85115aea9570bcb9e0c03c738b0eab7070cb5", size = 2191878, upload-time = "2024-10-02T13:00:02.441Z" }, + { url = "https://files.pythonhosted.org/packages/5d/84/52e622fed7335a97dbabf7cc577feefa92001d3d39135d3ddaef998bf929/python_crfsuite-0.9.11-cp310-cp310-win32.whl", hash = "sha256:b5a9492686e3dde5739ea19a3ec37397eb7cff787362e403a411acb6431aaf84", size = 280885, upload-time = "2024-10-02T13:00:04.186Z" }, + { url = "https://files.pythonhosted.org/packages/13/7a/def271af273f5b15052fa0ce8f80811b2486c47dc7c718971418b58c8ce2/python_crfsuite-0.9.11-cp310-cp310-win_amd64.whl", hash = "sha256:d2c361819ba331c48038f1b231b8863b886205e9decae2fb89f69da44b28d00a", size = 301530, upload-time = "2024-10-02T13:00:05.749Z" }, + { url = "https://files.pythonhosted.org/packages/bc/32/743048adf41ba3ebc4d82deed5d4a336164dc0066ef83b28d2a4b1979d66/python_crfsuite-0.9.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4a2f2ff5b6b0b6cf72ee476436f3926ccd0045c97e7703478a025c9badd180c6", size = 319684, upload-time = "2024-10-02T13:00:07.376Z" }, + { url = "https://files.pythonhosted.org/packages/f0/12/99235a88e786a4f2874cb9b416b53edc778719c2d27843c2727e7233bbaa/python_crfsuite-0.9.11-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83bc133fc2a411144778bb03d56a95f88a4da0386462fb99d32b45428959101f", size = 1211479, upload-time = "2024-10-02T13:00:08.624Z" }, + { url = "https://files.pythonhosted.org/packages/a5/7f/8da1653440925627f1e2730a2a8f048f9e84e644a95a6324f7cce5b0e324/python_crfsuite-0.9.11-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d5e52bfe54c1cb94009f1edb9c1dec3fe6d31823c60fafee04d63354c342303", size = 1244805, upload-time = "2024-10-02T13:00:10.406Z" }, + { url = "https://files.pythonhosted.org/packages/34/43/07221382ab31f3fd8d648c1421b240a332cc64ad7f89af275101ce9e5bad/python_crfsuite-0.9.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a23a96dc9a25a0d143430236158ca0d836b94a26d5752ffdf7efe315c14045f5", size = 1261854, upload-time = "2024-10-02T13:00:11.94Z" }, + { url = "https://files.pythonhosted.org/packages/45/e7/ac1d92ce9951401d71116526d76100a7ed3a1717dfbb2bfc0e9195d5ef09/python_crfsuite-0.9.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dd95a8ab9d92ac6756c17dde8150d7edcc696e49b4ca5f537e347143d19c94bc", size = 2139166, upload-time = "2024-10-02T13:00:13.789Z" }, + { url = "https://files.pythonhosted.org/packages/bd/d8/a4e799f6126a7b8bafe3896472065985c99d32a68dd9bba29885e1c3c56c/python_crfsuite-0.9.11-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:800fd345f2eb822d574eeaa6099bb88a23942272f62ea3e182e8ec07f4cf5ca8", size = 2282077, upload-time = "2024-10-02T13:00:15.116Z" }, + { url = "https://files.pythonhosted.org/packages/a7/0e/8f66fe63a751edd4ab1036af5276334e111a43d7cc939c57cd04090416d4/python_crfsuite-0.9.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4c17dc2c5ac63d10993afbab0288bb1949e4ac856361c83e8041fff4493d7dab", size = 2225963, upload-time = "2024-10-02T13:00:16.664Z" }, + { url = "https://files.pythonhosted.org/packages/50/57/8d58d869a41715fbefe6d4fcf62a483e76f223716f44583df7d2ac0a05fe/python_crfsuite-0.9.11-cp311-cp311-win32.whl", hash = "sha256:9a00f1f32203d9cb66658df75ee62ce4809b24f26b982b7f482934a683abc96c", size = 280872, upload-time = "2024-10-02T13:00:17.87Z" }, + { url = "https://files.pythonhosted.org/packages/f3/4c/2aabe6f3c06a6e62fb4d80d0ed224e3d813b7fc5bc7d9aba52e724639268/python_crfsuite-0.9.11-cp311-cp311-win_amd64.whl", hash = "sha256:d255f02c890628337c970d76cba787afb7991340b3a7b201d3a158add5f78989", size = 301866, upload-time = "2024-10-02T13:00:19.33Z" }, + { url = "https://files.pythonhosted.org/packages/22/e2/1fafca9ea7a964bbc4a8a91351acb1f46c17c63e9aa766a22a59b0a14aa0/python_crfsuite-0.9.11-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:346a37d1ffa9f161d56c523d2386eaa5026c663e70f65db4478adb292d7c047c", size = 319874, upload-time = "2024-10-02T13:00:20.921Z" }, + { url = "https://files.pythonhosted.org/packages/43/3f/1d954edbef65a0d2b9fe71a974973aaace5f617ab3276f50b995ea1ea198/python_crfsuite-0.9.11-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bec40a7924d2e79a06f8eb0cec613ade54d677b73c4041c6052cd890aca2db89", size = 1200041, upload-time = "2024-10-02T13:00:22.526Z" }, + { url = "https://files.pythonhosted.org/packages/49/77/069c75217707c0727944293571bccb90642269990abfac76427b3446c275/python_crfsuite-0.9.11-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c5b3836e8ee8d684fb9d76d287035db51039b30cadac3332664655acf970831", size = 1234277, upload-time = "2024-10-02T13:00:24.593Z" }, + { url = "https://files.pythonhosted.org/packages/54/a3/6af8a68bdfe4d8c001a1fdb84b6fa1154d2b772dbd619050697f4cc2fe39/python_crfsuite-0.9.11-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f498cb82686dc18f7cecaf0a7ebceb4590ee2137cfa8cfe1b75f53514d0e956", size = 1254759, upload-time = "2024-10-02T13:00:26.159Z" }, + { url = "https://files.pythonhosted.org/packages/6f/3c/75e26cfd1e2c8807aa465a7213f63e2e0c9d61af0ad0ff635bbd2f47e4aa/python_crfsuite-0.9.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:29cdf55c54c388c62148ba310bf8ad1b93b352d62dd84856d15c421dae2e902d", size = 2131213, upload-time = "2024-10-02T13:00:27.945Z" }, + { url = "https://files.pythonhosted.org/packages/16/76/53e57efd9f3e9cbbed3615d065d5367a8c18df0217e83ca216f215aa257a/python_crfsuite-0.9.11-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7e6738ed044ba91d8284716f87525ca95bc857ece0b226910a80126a8ce6ad06", size = 2259424, upload-time = "2024-10-02T13:00:29.371Z" }, + { url = "https://files.pythonhosted.org/packages/a1/05/663e01d5721ea5b0dfde0483696dd28498929cd095e86e31501891de2381/python_crfsuite-0.9.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1a365a70e54dbd20a9251a3b6df91e1406cab1b1b5995a9d68e8c748fc9b3af7", size = 2219484, upload-time = "2024-10-02T13:00:31.033Z" }, + { url = "https://files.pythonhosted.org/packages/75/53/55c908ca49e0d3f5d0c7410f876ef4fc0b605e140492b41f8833a1befb96/python_crfsuite-0.9.11-cp312-cp312-win32.whl", hash = "sha256:4b230ab1b69c6025e4f64e72c445f7492cccf00d94fc2c0bf2f337fafc05d5d5", size = 280500, upload-time = "2024-10-02T13:00:32.765Z" }, + { url = "https://files.pythonhosted.org/packages/54/08/d060405793cab77dbd167814393c838c456f9d9281cebcc3d9f185e2c5d5/python_crfsuite-0.9.11-cp312-cp312-win_amd64.whl", hash = "sha256:c89d7ad4ca520a5f045c676865ec09a2accc25dc5dce387f2199e5b2c9d8f337", size = 301055, upload-time = "2024-10-02T13:00:33.956Z" }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, +] + +[[package]] +name = "pytorch-lightning" +version = "2.5.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "fsspec", extra = ["http"] }, + { name = "lightning-utilities" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "torch" }, + { name = "torchmetrics" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0a/1f/94a441d30779e1ffa5f7dc2ac5fa374c142d8b96c347a49a30226264124e/pytorch_lightning-2.5.6.tar.gz", hash = "sha256:c428faaceef74be50b870814d0d7e9f9c6ee748b8769a2afd3366bc69daf3a0f", size = 642830, upload-time = "2025-11-05T20:53:04.871Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/17/e4/32ed2f33c1b634f7c2895369222f4f8cb345044f4642bbff718e7dd1e0b7/pytorch_lightning-2.5.6-py3-none-any.whl", hash = "sha256:037bad1e2fd94d5eb6c5144f045fd4c1070c3d38fc9c14d9f3774a3a9be54dff", size = 831555, upload-time = "2025-11-05T20:53:03.316Z" }, +] + +[[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" } +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 = "pyvi" +version = "0.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "scikit-learn" }, + { name = "sklearn-crfsuite" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/af/ce6ccb5458a7fed50127de2cac4ee47f88283062c859279e50de1e7cb7a9/pyvi-0.1.1.tar.gz", hash = "sha256:08abab45a83ad674cf6549725096b8530d49be51418aa2613ce1a0b45d17db2d", size = 8362510, upload-time = "2021-06-30T04:20:06.722Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/27/27ffee2663f42430cf3434da963f04224fec157b90799fe9e92a3564c1a6/pyvi-0.1.1-py2.py3-none-any.whl", hash = "sha256:e5e9e0e40ea1a556af12646c6b754f065342c86f1dfe84785ba926e1190c0bb7", size = 8455088, upload-time = "2021-06-30T04:20:01.957Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/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/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" }, + { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" }, + { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" }, + { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" }, + { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" }, + { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" }, + { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" }, + { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" }, + { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" }, + { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, + { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, + { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, + { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, + { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, + { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, + { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, + { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, + { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, + { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, +] + +[[package]] +name = "rank-bm25" +version = "0.2.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fc/0a/f9579384aa017d8b4c15613f86954b92a95a93d641cc849182467cf0bb3b/rank_bm25-0.2.2.tar.gz", hash = "sha256:096ccef76f8188563419aaf384a02f0ea459503fdf77901378d4fd9d87e5e51d", size = 8347, upload-time = "2022-02-16T12:10:52.196Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/21/f691fb2613100a62b3fa91e9988c991e9ca5b89ea31c0d3152a3210344f9/rank_bm25-0.2.2-py3-none-any.whl", hash = "sha256:7bd4a95571adadfc271746fa146a4bcfd89c0cf731e49c3d1ad863290adbe8ae", size = 8584, upload-time = "2022-02-16T12:10:50.626Z" }, +] + +[[package]] +name = "regex" +version = "2025.11.3" +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, upload-time = "2025-11-03T21:34:22.089Z" } +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, upload-time = "2025-11-03T21:30:47.317Z" }, + { 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, upload-time = "2025-11-03T21:30:49.912Z" }, + { 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, upload-time = "2025-11-03T21:30:51.344Z" }, + { 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, upload-time = "2025-11-03T21:30:52.596Z" }, + { 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, upload-time = "2025-11-03T21:30:53.825Z" }, + { 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, upload-time = "2025-11-03T21:30:55.575Z" }, + { 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, upload-time = "2025-11-03T21:30:57.262Z" }, + { 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, upload-time = "2025-11-03T21:30:58.788Z" }, + { 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, upload-time = "2025-11-03T21:31:00.081Z" }, + { 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, upload-time = "2025-11-03T21:31:01.583Z" }, + { 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, upload-time = "2025-11-03T21:31:03.244Z" }, + { 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, upload-time = "2025-11-03T21:31:04.876Z" }, + { url = "https://files.pythonhosted.org/packages/03/cc/90ab0fdbe6dce064a42015433f9152710139fb04a8b81b4fb57a1cb63ffa/regex-2025.11.3-cp310-cp310-win32.whl", hash = "sha256:149eb0bba95231fb4f6d37c8f760ec9fa6fabf65bab555e128dde5f2475193ec", size = 265802, upload-time = "2025-11-03T21:31:06.581Z" }, + { url = "https://files.pythonhosted.org/packages/34/9d/e9e8493a85f3b1ddc4a5014465f5c2b78c3ea1cbf238dcfde78956378041/regex-2025.11.3-cp310-cp310-win_amd64.whl", hash = "sha256:ee3a83ce492074c35a74cc76cf8235d49e77b757193a5365ff86e3f2f93db9fd", size = 277722, upload-time = "2025-11-03T21:31:08.144Z" }, + { url = "https://files.pythonhosted.org/packages/15/c4/b54b24f553966564506dbf873a3e080aef47b356a3b39b5d5aba992b50db/regex-2025.11.3-cp310-cp310-win_arm64.whl", hash = "sha256:38af559ad934a7b35147716655d4a2f79fcef2d695ddfe06a06ba40ae631fa7e", size = 270289, upload-time = "2025-11-03T21:31:10.267Z" }, + { url = "https://files.pythonhosted.org/packages/f7/90/4fb5056e5f03a7048abd2b11f598d464f0c167de4f2a51aa868c376b8c70/regex-2025.11.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:eadade04221641516fa25139273505a1c19f9bf97589a05bc4cfcd8b4a618031", size = 488081, upload-time = "2025-11-03T21:31:11.946Z" }, + { url = "https://files.pythonhosted.org/packages/85/23/63e481293fac8b069d84fba0299b6666df720d875110efd0338406b5d360/regex-2025.11.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:feff9e54ec0dd3833d659257f5c3f5322a12eee58ffa360984b716f8b92983f4", size = 290554, upload-time = "2025-11-03T21:31:13.387Z" }, + { url = "https://files.pythonhosted.org/packages/2b/9d/b101d0262ea293a0066b4522dfb722eb6a8785a8c3e084396a5f2c431a46/regex-2025.11.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3b30bc921d50365775c09a7ed446359e5c0179e9e2512beec4a60cbcef6ddd50", size = 288407, upload-time = "2025-11-03T21:31:14.809Z" }, + { url = "https://files.pythonhosted.org/packages/0c/64/79241c8209d5b7e00577ec9dca35cd493cc6be35b7d147eda367d6179f6d/regex-2025.11.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f99be08cfead2020c7ca6e396c13543baea32343b7a9a5780c462e323bd8872f", size = 793418, upload-time = "2025-11-03T21:31:16.556Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e2/23cd5d3573901ce8f9757c92ca4db4d09600b865919b6d3e7f69f03b1afd/regex-2025.11.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6dd329a1b61c0ee95ba95385fb0c07ea0d3fe1a21e1349fa2bec272636217118", size = 860448, upload-time = "2025-11-03T21:31:18.12Z" }, + { url = "https://files.pythonhosted.org/packages/2a/4c/aecf31beeaa416d0ae4ecb852148d38db35391aac19c687b5d56aedf3a8b/regex-2025.11.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4c5238d32f3c5269d9e87be0cf096437b7622b6920f5eac4fd202468aaeb34d2", size = 907139, upload-time = "2025-11-03T21:31:20.753Z" }, + { url = "https://files.pythonhosted.org/packages/61/22/b8cb00df7d2b5e0875f60628594d44dba283e951b1ae17c12f99e332cc0a/regex-2025.11.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:10483eefbfb0adb18ee9474498c9a32fcf4e594fbca0543bb94c48bac6183e2e", size = 800439, upload-time = "2025-11-03T21:31:22.069Z" }, + { url = "https://files.pythonhosted.org/packages/02/a8/c4b20330a5cdc7a8eb265f9ce593f389a6a88a0c5f280cf4d978f33966bc/regex-2025.11.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:78c2d02bb6e1da0720eedc0bad578049cad3f71050ef8cd065ecc87691bed2b0", size = 782965, upload-time = "2025-11-03T21:31:23.598Z" }, + { url = "https://files.pythonhosted.org/packages/b4/4c/ae3e52988ae74af4b04d2af32fee4e8077f26e51b62ec2d12d246876bea2/regex-2025.11.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e6b49cd2aad93a1790ce9cffb18964f6d3a4b0b3dbdbd5de094b65296fce6e58", size = 854398, upload-time = "2025-11-03T21:31:25.008Z" }, + { url = "https://files.pythonhosted.org/packages/06/d1/a8b9cf45874eda14b2e275157ce3b304c87e10fb38d9fc26a6e14eb18227/regex-2025.11.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:885b26aa3ee56433b630502dc3d36ba78d186a00cc535d3806e6bfd9ed3c70ab", size = 845897, upload-time = "2025-11-03T21:31:26.427Z" }, + { url = "https://files.pythonhosted.org/packages/ea/fe/1830eb0236be93d9b145e0bd8ab499f31602fe0999b1f19e99955aa8fe20/regex-2025.11.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ddd76a9f58e6a00f8772e72cff8ebcff78e022be95edf018766707c730593e1e", size = 788906, upload-time = "2025-11-03T21:31:28.078Z" }, + { url = "https://files.pythonhosted.org/packages/66/47/dc2577c1f95f188c1e13e2e69d8825a5ac582ac709942f8a03af42ed6e93/regex-2025.11.3-cp311-cp311-win32.whl", hash = "sha256:3e816cc9aac1cd3cc9a4ec4d860f06d40f994b5c7b4d03b93345f44e08cc68bf", size = 265812, upload-time = "2025-11-03T21:31:29.72Z" }, + { url = "https://files.pythonhosted.org/packages/50/1e/15f08b2f82a9bbb510621ec9042547b54d11e83cb620643ebb54e4eb7d71/regex-2025.11.3-cp311-cp311-win_amd64.whl", hash = "sha256:087511f5c8b7dfbe3a03f5d5ad0c2a33861b1fc387f21f6f60825a44865a385a", size = 277737, upload-time = "2025-11-03T21:31:31.422Z" }, + { url = "https://files.pythonhosted.org/packages/f4/fc/6500eb39f5f76c5e47a398df82e6b535a5e345f839581012a418b16f9cc3/regex-2025.11.3-cp311-cp311-win_arm64.whl", hash = "sha256:1ff0d190c7f68ae7769cd0313fe45820ba07ffebfddfaa89cc1eb70827ba0ddc", size = 270290, upload-time = "2025-11-03T21:31:33.041Z" }, + { url = "https://files.pythonhosted.org/packages/e8/74/18f04cb53e58e3fb107439699bd8375cf5a835eec81084e0bddbd122e4c2/regex-2025.11.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bc8ab71e2e31b16e40868a40a69007bc305e1109bd4658eb6cad007e0bf67c41", size = 489312, upload-time = "2025-11-03T21:31:34.343Z" }, + { url = "https://files.pythonhosted.org/packages/78/3f/37fcdd0d2b1e78909108a876580485ea37c91e1acf66d3bb8e736348f441/regex-2025.11.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:22b29dda7e1f7062a52359fca6e58e548e28c6686f205e780b02ad8ef710de36", size = 291256, upload-time = "2025-11-03T21:31:35.675Z" }, + { url = "https://files.pythonhosted.org/packages/bf/26/0a575f58eb23b7ebd67a45fccbc02ac030b737b896b7e7a909ffe43ffd6a/regex-2025.11.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3a91e4a29938bc1a082cc28fdea44be420bf2bebe2665343029723892eb073e1", size = 288921, upload-time = "2025-11-03T21:31:37.07Z" }, + { url = "https://files.pythonhosted.org/packages/ea/98/6a8dff667d1af907150432cf5abc05a17ccd32c72a3615410d5365ac167a/regex-2025.11.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08b884f4226602ad40c5d55f52bf91a9df30f513864e0054bad40c0e9cf1afb7", size = 798568, upload-time = "2025-11-03T21:31:38.784Z" }, + { url = "https://files.pythonhosted.org/packages/64/15/92c1db4fa4e12733dd5a526c2dd2b6edcbfe13257e135fc0f6c57f34c173/regex-2025.11.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3e0b11b2b2433d1c39c7c7a30e3f3d0aeeea44c2a8d0bae28f6b95f639927a69", size = 864165, upload-time = "2025-11-03T21:31:40.559Z" }, + { url = "https://files.pythonhosted.org/packages/f9/e7/3ad7da8cdee1ce66c7cd37ab5ab05c463a86ffeb52b1a25fe7bd9293b36c/regex-2025.11.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:87eb52a81ef58c7ba4d45c3ca74e12aa4b4e77816f72ca25258a85b3ea96cb48", size = 912182, upload-time = "2025-11-03T21:31:42.002Z" }, + { url = "https://files.pythonhosted.org/packages/84/bd/9ce9f629fcb714ffc2c3faf62b6766ecb7a585e1e885eb699bcf130a5209/regex-2025.11.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a12ab1f5c29b4e93db518f5e3872116b7e9b1646c9f9f426f777b50d44a09e8c", size = 803501, upload-time = "2025-11-03T21:31:43.815Z" }, + { url = "https://files.pythonhosted.org/packages/7c/0f/8dc2e4349d8e877283e6edd6c12bdcebc20f03744e86f197ab6e4492bf08/regex-2025.11.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7521684c8c7c4f6e88e35ec89680ee1aa8358d3f09d27dfbdf62c446f5d4c695", size = 787842, upload-time = "2025-11-03T21:31:45.353Z" }, + { url = "https://files.pythonhosted.org/packages/f9/73/cff02702960bc185164d5619c0c62a2f598a6abff6695d391b096237d4ab/regex-2025.11.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7fe6e5440584e94cc4b3f5f4d98a25e29ca12dccf8873679a635638349831b98", size = 858519, upload-time = "2025-11-03T21:31:46.814Z" }, + { url = "https://files.pythonhosted.org/packages/61/83/0e8d1ae71e15bc1dc36231c90b46ee35f9d52fab2e226b0e039e7ea9c10a/regex-2025.11.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:8e026094aa12b43f4fd74576714e987803a315c76edb6b098b9809db5de58f74", size = 850611, upload-time = "2025-11-03T21:31:48.289Z" }, + { url = "https://files.pythonhosted.org/packages/c8/f5/70a5cdd781dcfaa12556f2955bf170cd603cb1c96a1827479f8faea2df97/regex-2025.11.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:435bbad13e57eb5606a68443af62bed3556de2f46deb9f7d4237bc2f1c9fb3a0", size = 789759, upload-time = "2025-11-03T21:31:49.759Z" }, + { url = "https://files.pythonhosted.org/packages/59/9b/7c29be7903c318488983e7d97abcf8ebd3830e4c956c4c540005fcfb0462/regex-2025.11.3-cp312-cp312-win32.whl", hash = "sha256:3839967cf4dc4b985e1570fd8d91078f0c519f30491c60f9ac42a8db039be204", size = 266194, upload-time = "2025-11-03T21:31:51.53Z" }, + { url = "https://files.pythonhosted.org/packages/1a/67/3b92df89f179d7c367be654ab5626ae311cb28f7d5c237b6bb976cd5fbbb/regex-2025.11.3-cp312-cp312-win_amd64.whl", hash = "sha256:e721d1b46e25c481dc5ded6f4b3f66c897c58d2e8cfdf77bbced84339108b0b9", size = 277069, upload-time = "2025-11-03T21:31:53.151Z" }, + { url = "https://files.pythonhosted.org/packages/d7/55/85ba4c066fe5094d35b249c3ce8df0ba623cfd35afb22d6764f23a52a1c5/regex-2025.11.3-cp312-cp312-win_arm64.whl", hash = "sha256:64350685ff08b1d3a6fff33f45a9ca183dc1d58bbfe4981604e70ec9801bbc26", size = 270330, upload-time = "2025-11-03T21:31:54.514Z" }, +] + +[[package]] +name = "requests" +version = "2.28.1" +source = { registry = "https://download.pytorch.org/whl/cu126" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +wheels = [ + { url = "https://download.pytorch.org/whl/requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349" }, +] + +[package.optional-dependencies] +socks = [ + { name = "pysocks" }, +] + +[[package]] +name = "rich" +version = "14.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b3/c6/f3b320c27991c46f43ee9d856302c70dc2d0fb2dba4842ff739d5f46b393/rich-14.3.3.tar.gz", hash = "sha256:b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b", size = 230582, upload-time = "2026-02-19T17:23:12.474Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl", hash = "sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d", size = 310458, upload-time = "2026-02-19T17:23:13.732Z" }, +] + +[[package]] +name = "ruff" +version = "0.15.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/22/9e4f66ee588588dc6c9af6a994e12d26e19efbe874d1a909d09a6dac7a59/ruff-0.15.7.tar.gz", hash = "sha256:04f1ae61fc20fe0b148617c324d9d009b5f63412c0b16474f3d5f1a1a665f7ac", size = 4601277, upload-time = "2026-03-19T16:26:22.605Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/2f/0b08ced94412af091807b6119ca03755d651d3d93a242682bf020189db94/ruff-0.15.7-py3-none-linux_armv6l.whl", hash = "sha256:a81cc5b6910fb7dfc7c32d20652e50fa05963f6e13ead3c5915c41ac5d16668e", size = 10489037, upload-time = "2026-03-19T16:26:32.47Z" }, + { url = "https://files.pythonhosted.org/packages/91/4a/82e0fa632e5c8b1eba5ee86ecd929e8ff327bbdbfb3c6ac5d81631bef605/ruff-0.15.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:722d165bd52403f3bdabc0ce9e41fc47070ac56d7a91b4e0d097b516a53a3477", size = 10955433, upload-time = "2026-03-19T16:27:00.205Z" }, + { url = "https://files.pythonhosted.org/packages/ab/10/12586735d0ff42526ad78c049bf51d7428618c8b5c467e72508c694119df/ruff-0.15.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7fbc2448094262552146cbe1b9643a92f66559d3761f1ad0656d4991491af49e", size = 10269302, upload-time = "2026-03-19T16:26:26.183Z" }, + { url = "https://files.pythonhosted.org/packages/eb/5d/32b5c44ccf149a26623671df49cbfbd0a0ae511ff3df9d9d2426966a8d57/ruff-0.15.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b39329b60eba44156d138275323cc726bbfbddcec3063da57caa8a8b1d50adf", size = 10607625, upload-time = "2026-03-19T16:27:03.263Z" }, + { url = "https://files.pythonhosted.org/packages/5d/f1/f0001cabe86173aaacb6eb9bb734aa0605f9a6aa6fa7d43cb49cbc4af9c9/ruff-0.15.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:87768c151808505f2bfc93ae44e5f9e7c8518943e5074f76ac21558ef5627c85", size = 10324743, upload-time = "2026-03-19T16:27:09.791Z" }, + { url = "https://files.pythonhosted.org/packages/7a/87/b8a8f3d56b8d848008559e7c9d8bf367934d5367f6d932ba779456e2f73b/ruff-0.15.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fb0511670002c6c529ec66c0e30641c976c8963de26a113f3a30456b702468b0", size = 11138536, upload-time = "2026-03-19T16:27:06.101Z" }, + { url = "https://files.pythonhosted.org/packages/e4/f2/4fd0d05aab0c5934b2e1464784f85ba2eab9d54bffc53fb5430d1ed8b829/ruff-0.15.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e0d19644f801849229db8345180a71bee5407b429dd217f853ec515e968a6912", size = 11994292, upload-time = "2026-03-19T16:26:48.718Z" }, + { url = "https://files.pythonhosted.org/packages/64/22/fc4483871e767e5e95d1622ad83dad5ebb830f762ed0420fde7dfa9d9b08/ruff-0.15.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4806d8e09ef5e84eb19ba833d0442f7e300b23fe3f0981cae159a248a10f0036", size = 11398981, upload-time = "2026-03-19T16:26:54.513Z" }, + { url = "https://files.pythonhosted.org/packages/b0/99/66f0343176d5eab02c3f7fcd2de7a8e0dd7a41f0d982bee56cd1c24db62b/ruff-0.15.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dce0896488562f09a27b9c91b1f58a097457143931f3c4d519690dea54e624c5", size = 11242422, upload-time = "2026-03-19T16:26:29.277Z" }, + { url = "https://files.pythonhosted.org/packages/5d/3a/a7060f145bfdcce4c987ea27788b30c60e2c81d6e9a65157ca8afe646328/ruff-0.15.7-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:1852ce241d2bc89e5dc823e03cff4ce73d816b5c6cdadd27dbfe7b03217d2a12", size = 11232158, upload-time = "2026-03-19T16:26:42.321Z" }, + { url = "https://files.pythonhosted.org/packages/a7/53/90fbb9e08b29c048c403558d3cdd0adf2668b02ce9d50602452e187cd4af/ruff-0.15.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:5f3e4b221fb4bd293f79912fc5e93a9063ebd6d0dcbd528f91b89172a9b8436c", size = 10577861, upload-time = "2026-03-19T16:26:57.459Z" }, + { url = "https://files.pythonhosted.org/packages/2f/aa/5f486226538fe4d0f0439e2da1716e1acf895e2a232b26f2459c55f8ddad/ruff-0.15.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b15e48602c9c1d9bdc504b472e90b90c97dc7d46c7028011ae67f3861ceba7b4", size = 10327310, upload-time = "2026-03-19T16:26:35.909Z" }, + { url = "https://files.pythonhosted.org/packages/99/9e/271afdffb81fe7bfc8c43ba079e9d96238f674380099457a74ccb3863857/ruff-0.15.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1b4705e0e85cedc74b0a23cf6a179dbb3df184cb227761979cc76c0440b5ab0d", size = 10840752, upload-time = "2026-03-19T16:26:45.723Z" }, + { url = "https://files.pythonhosted.org/packages/bf/29/a4ae78394f76c7759953c47884eb44de271b03a66634148d9f7d11e721bd/ruff-0.15.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:112c1fa316a558bb34319282c1200a8bf0495f1b735aeb78bfcb2991e6087580", size = 11336961, upload-time = "2026-03-19T16:26:39.076Z" }, + { url = "https://files.pythonhosted.org/packages/26/6b/8786ba5736562220d588a2f6653e6c17e90c59ced34a2d7b512ef8956103/ruff-0.15.7-py3-none-win32.whl", hash = "sha256:6d39e2d3505b082323352f733599f28169d12e891f7dd407f2d4f54b4c2886de", size = 10582538, upload-time = "2026-03-19T16:26:15.992Z" }, + { url = "https://files.pythonhosted.org/packages/2b/e9/346d4d3fffc6871125e877dae8d9a1966b254fbd92a50f8561078b88b099/ruff-0.15.7-py3-none-win_amd64.whl", hash = "sha256:4d53d712ddebcd7dace1bc395367aec12c057aacfe9adbb6d832302575f4d3a1", size = 11755839, upload-time = "2026-03-19T16:26:19.897Z" }, + { url = "https://files.pythonhosted.org/packages/8f/e8/726643a3ea68c727da31570bde48c7a10f1aa60eddd628d94078fec586ff/ruff-0.15.7-py3-none-win_arm64.whl", hash = "sha256:18e8d73f1c3fdf27931497972250340f92e8c861722161a9caeb89a58ead6ed2", size = 11023304, upload-time = "2026-03-19T16:26:51.669Z" }, +] + +[[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" }, +] + +[[package]] +name = "scikit-learn" +version = "1.7.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "joblib" }, + { name = "numpy" }, + { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scipy", version = "1.16.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { 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" } +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/43/83/564e141eef908a5863a54da8ca342a137f45a0bfb71d1d79704c9894c9d1/scikit_learn-1.7.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7509693451651cd7361d30ce4e86a1347493554f172b1c72a39300fa2aea79e", size = 9331967, upload-time = "2025-09-09T08:20:32.421Z" }, + { url = "https://files.pythonhosted.org/packages/18/d6/ba863a4171ac9d7314c4d3fc251f015704a2caeee41ced89f321c049ed83/scikit_learn-1.7.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:0486c8f827c2e7b64837c731c8feff72c0bd2b998067a8a9cbc10643c31f0fe1", size = 8648645, upload-time = "2025-09-09T08:20:34.436Z" }, + { url = "https://files.pythonhosted.org/packages/ef/0e/97dbca66347b8cf0ea8b529e6bb9367e337ba2e8be0ef5c1a545232abfde/scikit_learn-1.7.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:89877e19a80c7b11a2891a27c21c4894fb18e2c2e077815bcade10d34287b20d", size = 9715424, upload-time = "2025-09-09T08:20:36.776Z" }, + { url = "https://files.pythonhosted.org/packages/f7/32/1f3b22e3207e1d2c883a7e09abb956362e7d1bd2f14458c7de258a26ac15/scikit_learn-1.7.2-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8da8bf89d4d79aaec192d2bda62f9b56ae4e5b4ef93b6a56b5de4977e375c1f1", size = 9509234, upload-time = "2025-09-09T08:20:38.957Z" }, + { url = "https://files.pythonhosted.org/packages/9f/71/34ddbd21f1da67c7a768146968b4d0220ee6831e4bcbad3e03dd3eae88b6/scikit_learn-1.7.2-cp311-cp311-win_amd64.whl", hash = "sha256:9b7ed8d58725030568523e937c43e56bc01cadb478fc43c042a9aca1dacb3ba1", size = 8894244, upload-time = "2025-09-09T08:20:41.166Z" }, + { url = "https://files.pythonhosted.org/packages/a7/aa/3996e2196075689afb9fce0410ebdb4a09099d7964d061d7213700204409/scikit_learn-1.7.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8d91a97fa2b706943822398ab943cde71858a50245e31bc71dba62aab1d60a96", size = 9259818, upload-time = "2025-09-09T08:20:43.19Z" }, + { url = "https://files.pythonhosted.org/packages/43/5d/779320063e88af9c4a7c2cf463ff11c21ac9c8bd730c4a294b0000b666c9/scikit_learn-1.7.2-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:acbc0f5fd2edd3432a22c69bed78e837c70cf896cd7993d71d51ba6708507476", size = 8636997, upload-time = "2025-09-09T08:20:45.468Z" }, + { url = "https://files.pythonhosted.org/packages/5c/d0/0c577d9325b05594fdd33aa970bf53fb673f051a45496842caee13cfd7fe/scikit_learn-1.7.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e5bf3d930aee75a65478df91ac1225ff89cd28e9ac7bd1196853a9229b6adb0b", size = 9478381, upload-time = "2025-09-09T08:20:47.982Z" }, + { url = "https://files.pythonhosted.org/packages/82/70/8bf44b933837ba8494ca0fc9a9ab60f1c13b062ad0197f60a56e2fc4c43e/scikit_learn-1.7.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4d6e9deed1a47aca9fe2f267ab8e8fe82ee20b4526b2c0cd9e135cea10feb44", size = 9300296, upload-time = "2025-09-09T08:20:50.366Z" }, + { url = "https://files.pythonhosted.org/packages/c6/99/ed35197a158f1fdc2fe7c3680e9c70d0128f662e1fee4ed495f4b5e13db0/scikit_learn-1.7.2-cp312-cp312-win_amd64.whl", hash = "sha256:6088aa475f0785e01bcf8529f55280a3d7d298679f50c0bb70a2364a82d0b290", size = 8731256, upload-time = "2025-09-09T08:20:52.627Z" }, +] + +[[package]] +name = "scikit-plot" +version = "0.3.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "joblib" }, + { name = "matplotlib" }, + { name = "scikit-learn" }, + { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scipy", version = "1.16.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/58/66/ad1c1493946cac000d3f585e9ce5e1434140b6947fd3829d22a766933e53/scikit-plot-0.3.7.tar.gz", hash = "sha256:2c7948817fd2dc06879cfe3c1fdde56a8e71fa5ac626ffbe79f043650baa6242", size = 28615, upload-time = "2018-08-19T12:18:50.524Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/47/32520e259340c140a4ad27c1b97050dd3254fdc517b1d59974d47037510e/scikit_plot-0.3.7-py3-none-any.whl", hash = "sha256:6b3d529800b32a899c54dc5761a93c63cbff482b1889a4afee57dd219f3ef0c3", size = 33679, upload-time = "2018-08-19T12:18:46.947Z" }, +] + +[[package]] +name = "scipy" +version = "1.15.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'linux'", + "python_full_version < '3.11' and sys_platform == 'darwin'", + "python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux'", +] +dependencies = [ + { name = "numpy", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0f/37/6964b830433e654ec7485e45a00fc9a27cf868d622838f6b6d9c5ec0d532/scipy-1.15.3.tar.gz", hash = "sha256:eae3cf522bc7df64b42cad3925c876e1b0b6c35c1337c93e12c0f366f55b0eaf", size = 59419214, upload-time = "2025-05-08T16:13:05.955Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/2f/4966032c5f8cc7e6a60f1b2e0ad686293b9474b65246b0c642e3ef3badd0/scipy-1.15.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:a345928c86d535060c9c2b25e71e87c39ab2f22fc96e9636bd74d1dbf9de448c", size = 38702770, upload-time = "2025-05-08T16:04:20.849Z" }, + { url = "https://files.pythonhosted.org/packages/a0/6e/0c3bf90fae0e910c274db43304ebe25a6b391327f3f10b5dcc638c090795/scipy-1.15.3-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:ad3432cb0f9ed87477a8d97f03b763fd1d57709f1bbde3c9369b1dff5503b253", size = 30094511, upload-time = "2025-05-08T16:04:27.103Z" }, + { url = "https://files.pythonhosted.org/packages/ea/b1/4deb37252311c1acff7f101f6453f0440794f51b6eacb1aad4459a134081/scipy-1.15.3-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:aef683a9ae6eb00728a542b796f52a5477b78252edede72b8327a886ab63293f", size = 22368151, upload-time = "2025-05-08T16:04:31.731Z" }, + { url = "https://files.pythonhosted.org/packages/38/7d/f457626e3cd3c29b3a49ca115a304cebb8cc6f31b04678f03b216899d3c6/scipy-1.15.3-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:1c832e1bd78dea67d5c16f786681b28dd695a8cb1fb90af2e27580d3d0967e92", size = 25121732, upload-time = "2025-05-08T16:04:36.596Z" }, + { url = "https://files.pythonhosted.org/packages/db/0a/92b1de4a7adc7a15dcf5bddc6e191f6f29ee663b30511ce20467ef9b82e4/scipy-1.15.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:263961f658ce2165bbd7b99fa5135195c3a12d9bef045345016b8b50c315cb82", size = 35547617, upload-time = "2025-05-08T16:04:43.546Z" }, + { url = "https://files.pythonhosted.org/packages/8e/6d/41991e503e51fc1134502694c5fa7a1671501a17ffa12716a4a9151af3df/scipy-1.15.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e2abc762b0811e09a0d3258abee2d98e0c703eee49464ce0069590846f31d40", size = 37662964, upload-time = "2025-05-08T16:04:49.431Z" }, + { url = "https://files.pythonhosted.org/packages/25/e1/3df8f83cb15f3500478c889be8fb18700813b95e9e087328230b98d547ff/scipy-1.15.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ed7284b21a7a0c8f1b6e5977ac05396c0d008b89e05498c8b7e8f4a1423bba0e", size = 37238749, upload-time = "2025-05-08T16:04:55.215Z" }, + { url = "https://files.pythonhosted.org/packages/93/3e/b3257cf446f2a3533ed7809757039016b74cd6f38271de91682aa844cfc5/scipy-1.15.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5380741e53df2c566f4d234b100a484b420af85deb39ea35a1cc1be84ff53a5c", size = 40022383, upload-time = "2025-05-08T16:05:01.914Z" }, + { url = "https://files.pythonhosted.org/packages/d1/84/55bc4881973d3f79b479a5a2e2df61c8c9a04fcb986a213ac9c02cfb659b/scipy-1.15.3-cp310-cp310-win_amd64.whl", hash = "sha256:9d61e97b186a57350f6d6fd72640f9e99d5a4a2b8fbf4b9ee9a841eab327dc13", size = 41259201, upload-time = "2025-05-08T16:05:08.166Z" }, + { url = "https://files.pythonhosted.org/packages/96/ab/5cc9f80f28f6a7dff646c5756e559823614a42b1939d86dd0ed550470210/scipy-1.15.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:993439ce220d25e3696d1b23b233dd010169b62f6456488567e830654ee37a6b", size = 38714255, upload-time = "2025-05-08T16:05:14.596Z" }, + { url = "https://files.pythonhosted.org/packages/4a/4a/66ba30abe5ad1a3ad15bfb0b59d22174012e8056ff448cb1644deccbfed2/scipy-1.15.3-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:34716e281f181a02341ddeaad584205bd2fd3c242063bd3423d61ac259ca7eba", size = 30111035, upload-time = "2025-05-08T16:05:20.152Z" }, + { url = "https://files.pythonhosted.org/packages/4b/fa/a7e5b95afd80d24313307f03624acc65801846fa75599034f8ceb9e2cbf6/scipy-1.15.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3b0334816afb8b91dab859281b1b9786934392aa3d527cd847e41bb6f45bee65", size = 22384499, upload-time = "2025-05-08T16:05:24.494Z" }, + { url = "https://files.pythonhosted.org/packages/17/99/f3aaddccf3588bb4aea70ba35328c204cadd89517a1612ecfda5b2dd9d7a/scipy-1.15.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:6db907c7368e3092e24919b5e31c76998b0ce1684d51a90943cb0ed1b4ffd6c1", size = 25152602, upload-time = "2025-05-08T16:05:29.313Z" }, + { url = "https://files.pythonhosted.org/packages/56/c5/1032cdb565f146109212153339f9cb8b993701e9fe56b1c97699eee12586/scipy-1.15.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:721d6b4ef5dc82ca8968c25b111e307083d7ca9091bc38163fb89243e85e3889", size = 35503415, upload-time = "2025-05-08T16:05:34.699Z" }, + { url = "https://files.pythonhosted.org/packages/bd/37/89f19c8c05505d0601ed5650156e50eb881ae3918786c8fd7262b4ee66d3/scipy-1.15.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39cb9c62e471b1bb3750066ecc3a3f3052b37751c7c3dfd0fd7e48900ed52982", size = 37652622, upload-time = "2025-05-08T16:05:40.762Z" }, + { url = "https://files.pythonhosted.org/packages/7e/31/be59513aa9695519b18e1851bb9e487de66f2d31f835201f1b42f5d4d475/scipy-1.15.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:795c46999bae845966368a3c013e0e00947932d68e235702b5c3f6ea799aa8c9", size = 37244796, upload-time = "2025-05-08T16:05:48.119Z" }, + { url = "https://files.pythonhosted.org/packages/10/c0/4f5f3eeccc235632aab79b27a74a9130c6c35df358129f7ac8b29f562ac7/scipy-1.15.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:18aaacb735ab38b38db42cb01f6b92a2d0d4b6aabefeb07f02849e47f8fb3594", size = 40047684, upload-time = "2025-05-08T16:05:54.22Z" }, + { url = "https://files.pythonhosted.org/packages/ab/a7/0ddaf514ce8a8714f6ed243a2b391b41dbb65251affe21ee3077ec45ea9a/scipy-1.15.3-cp311-cp311-win_amd64.whl", hash = "sha256:ae48a786a28412d744c62fd7816a4118ef97e5be0bee968ce8f0a2fba7acf3bb", size = 41246504, upload-time = "2025-05-08T16:06:00.437Z" }, + { url = "https://files.pythonhosted.org/packages/37/4b/683aa044c4162e10ed7a7ea30527f2cbd92e6999c10a8ed8edb253836e9c/scipy-1.15.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6ac6310fdbfb7aa6612408bd2f07295bcbd3fda00d2d702178434751fe48e019", size = 38766735, upload-time = "2025-05-08T16:06:06.471Z" }, + { url = "https://files.pythonhosted.org/packages/7b/7e/f30be3d03de07f25dc0ec926d1681fed5c732d759ac8f51079708c79e680/scipy-1.15.3-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:185cd3d6d05ca4b44a8f1595af87f9c372bb6acf9c808e99aa3e9aa03bd98cf6", size = 30173284, upload-time = "2025-05-08T16:06:11.686Z" }, + { url = "https://files.pythonhosted.org/packages/07/9c/0ddb0d0abdabe0d181c1793db51f02cd59e4901da6f9f7848e1f96759f0d/scipy-1.15.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:05dc6abcd105e1a29f95eada46d4a3f251743cfd7d3ae8ddb4088047f24ea477", size = 22446958, upload-time = "2025-05-08T16:06:15.97Z" }, + { url = "https://files.pythonhosted.org/packages/af/43/0bce905a965f36c58ff80d8bea33f1f9351b05fad4beaad4eae34699b7a1/scipy-1.15.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:06efcba926324df1696931a57a176c80848ccd67ce6ad020c810736bfd58eb1c", size = 25242454, upload-time = "2025-05-08T16:06:20.394Z" }, + { url = "https://files.pythonhosted.org/packages/56/30/a6f08f84ee5b7b28b4c597aca4cbe545535c39fe911845a96414700b64ba/scipy-1.15.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05045d8b9bfd807ee1b9f38761993297b10b245f012b11b13b91ba8945f7e45", size = 35210199, upload-time = "2025-05-08T16:06:26.159Z" }, + { url = "https://files.pythonhosted.org/packages/0b/1f/03f52c282437a168ee2c7c14a1a0d0781a9a4a8962d84ac05c06b4c5b555/scipy-1.15.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:271e3713e645149ea5ea3e97b57fdab61ce61333f97cfae392c28ba786f9bb49", size = 37309455, upload-time = "2025-05-08T16:06:32.778Z" }, + { url = "https://files.pythonhosted.org/packages/89/b1/fbb53137f42c4bf630b1ffdfc2151a62d1d1b903b249f030d2b1c0280af8/scipy-1.15.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6cfd56fc1a8e53f6e89ba3a7a7251f7396412d655bca2aa5611c8ec9a6784a1e", size = 36885140, upload-time = "2025-05-08T16:06:39.249Z" }, + { url = "https://files.pythonhosted.org/packages/2e/2e/025e39e339f5090df1ff266d021892694dbb7e63568edcfe43f892fa381d/scipy-1.15.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0ff17c0bb1cb32952c09217d8d1eed9b53d1463e5f1dd6052c7857f83127d539", size = 39710549, upload-time = "2025-05-08T16:06:45.729Z" }, + { url = "https://files.pythonhosted.org/packages/e6/eb/3bf6ea8ab7f1503dca3a10df2e4b9c3f6b3316df07f6c0ded94b281c7101/scipy-1.15.3-cp312-cp312-win_amd64.whl", hash = "sha256:52092bc0472cfd17df49ff17e70624345efece4e1a12b23783a1ac59a1b728ed", size = 40966184, upload-time = "2025-05-08T16:06:52.623Z" }, +] + +[[package]] +name = "scipy" +version = "1.16.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "python_full_version >= '3.12' and platform_machine != 'aarch64' and sys_platform == 'linux'", + "python_full_version >= '3.12' and sys_platform == 'darwin'", + "python_full_version >= '3.12' and sys_platform != 'darwin' and sys_platform != 'linux'", + "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "python_full_version == '3.11.*' and platform_machine != 'aarch64' and sys_platform == 'linux'", + "python_full_version == '3.11.*' and sys_platform == 'darwin'", + "python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux'", +] +dependencies = [ + { name = "numpy", marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0a/ca/d8ace4f98322d01abcd52d381134344bf7b431eba7ed8b42bdea5a3c2ac9/scipy-1.16.3.tar.gz", hash = "sha256:01e87659402762f43bd2fee13370553a17ada367d42e7487800bf2916535aecb", size = 30597883, upload-time = "2025-10-28T17:38:54.068Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9b/5f/6f37d7439de1455ce9c5a556b8d1db0979f03a796c030bafdf08d35b7bf9/scipy-1.16.3-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:40be6cf99e68b6c4321e9f8782e7d5ff8265af28ef2cd56e9c9b2638fa08ad97", size = 36630881, upload-time = "2025-10-28T17:31:47.104Z" }, + { url = "https://files.pythonhosted.org/packages/7c/89/d70e9f628749b7e4db2aa4cd89735502ff3f08f7b9b27d2e799485987cd9/scipy-1.16.3-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:8be1ca9170fcb6223cc7c27f4305d680ded114a1567c0bd2bfcbf947d1b17511", size = 28941012, upload-time = "2025-10-28T17:31:53.411Z" }, + { url = "https://files.pythonhosted.org/packages/a8/a8/0e7a9a6872a923505dbdf6bb93451edcac120363131c19013044a1e7cb0c/scipy-1.16.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:bea0a62734d20d67608660f69dcda23e7f90fb4ca20974ab80b6ed40df87a005", size = 20931935, upload-time = "2025-10-28T17:31:57.361Z" }, + { url = "https://files.pythonhosted.org/packages/bd/c7/020fb72bd79ad798e4dbe53938543ecb96b3a9ac3fe274b7189e23e27353/scipy-1.16.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:2a207a6ce9c24f1951241f4693ede2d393f59c07abc159b2cb2be980820e01fb", size = 23534466, upload-time = "2025-10-28T17:32:01.875Z" }, + { url = "https://files.pythonhosted.org/packages/be/a0/668c4609ce6dbf2f948e167836ccaf897f95fb63fa231c87da7558a374cd/scipy-1.16.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:532fb5ad6a87e9e9cd9c959b106b73145a03f04c7d57ea3e6f6bb60b86ab0876", size = 33593618, upload-time = "2025-10-28T17:32:06.902Z" }, + { url = "https://files.pythonhosted.org/packages/ca/6e/8942461cf2636cdae083e3eb72622a7fbbfa5cf559c7d13ab250a5dbdc01/scipy-1.16.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0151a0749efeaaab78711c78422d413c583b8cdd2011a3c1d6c794938ee9fdb2", size = 35899798, upload-time = "2025-10-28T17:32:12.665Z" }, + { url = "https://files.pythonhosted.org/packages/79/e8/d0f33590364cdbd67f28ce79368b373889faa4ee959588beddf6daef9abe/scipy-1.16.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b7180967113560cca57418a7bc719e30366b47959dd845a93206fbed693c867e", size = 36226154, upload-time = "2025-10-28T17:32:17.961Z" }, + { url = "https://files.pythonhosted.org/packages/39/c1/1903de608c0c924a1749c590064e65810f8046e437aba6be365abc4f7557/scipy-1.16.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:deb3841c925eeddb6afc1e4e4a45e418d19ec7b87c5df177695224078e8ec733", size = 38878540, upload-time = "2025-10-28T17:32:23.907Z" }, + { url = "https://files.pythonhosted.org/packages/f1/d0/22ec7036ba0b0a35bccb7f25ab407382ed34af0b111475eb301c16f8a2e5/scipy-1.16.3-cp311-cp311-win_amd64.whl", hash = "sha256:53c3844d527213631e886621df5695d35e4f6a75f620dca412bcd292f6b87d78", size = 38722107, upload-time = "2025-10-28T17:32:29.921Z" }, + { url = "https://files.pythonhosted.org/packages/7b/60/8a00e5a524bb3bf8898db1650d350f50e6cffb9d7a491c561dc9826c7515/scipy-1.16.3-cp311-cp311-win_arm64.whl", hash = "sha256:9452781bd879b14b6f055b26643703551320aa8d79ae064a71df55c00286a184", size = 25506272, upload-time = "2025-10-28T17:32:34.577Z" }, + { url = "https://files.pythonhosted.org/packages/40/41/5bf55c3f386b1643812f3a5674edf74b26184378ef0f3e7c7a09a7e2ca7f/scipy-1.16.3-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:81fc5827606858cf71446a5e98715ba0e11f0dbc83d71c7409d05486592a45d6", size = 36659043, upload-time = "2025-10-28T17:32:40.285Z" }, + { url = "https://files.pythonhosted.org/packages/1e/0f/65582071948cfc45d43e9870bf7ca5f0e0684e165d7c9ef4e50d783073eb/scipy-1.16.3-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:c97176013d404c7346bf57874eaac5187d969293bf40497140b0a2b2b7482e07", size = 28898986, upload-time = "2025-10-28T17:32:45.325Z" }, + { url = "https://files.pythonhosted.org/packages/96/5e/36bf3f0ac298187d1ceadde9051177d6a4fe4d507e8f59067dc9dd39e650/scipy-1.16.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:2b71d93c8a9936046866acebc915e2af2e292b883ed6e2cbe5c34beb094b82d9", size = 20889814, upload-time = "2025-10-28T17:32:49.277Z" }, + { url = "https://files.pythonhosted.org/packages/80/35/178d9d0c35394d5d5211bbff7ac4f2986c5488b59506fef9e1de13ea28d3/scipy-1.16.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:3d4a07a8e785d80289dfe66b7c27d8634a773020742ec7187b85ccc4b0e7b686", size = 23565795, upload-time = "2025-10-28T17:32:53.337Z" }, + { url = "https://files.pythonhosted.org/packages/fa/46/d1146ff536d034d02f83c8afc3c4bab2eddb634624d6529a8512f3afc9da/scipy-1.16.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0553371015692a898e1aa858fed67a3576c34edefa6b7ebdb4e9dde49ce5c203", size = 33349476, upload-time = "2025-10-28T17:32:58.353Z" }, + { url = "https://files.pythonhosted.org/packages/79/2e/415119c9ab3e62249e18c2b082c07aff907a273741b3f8160414b0e9193c/scipy-1.16.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:72d1717fd3b5e6ec747327ce9bda32d5463f472c9dce9f54499e81fbd50245a1", size = 35676692, upload-time = "2025-10-28T17:33:03.88Z" }, + { url = "https://files.pythonhosted.org/packages/27/82/df26e44da78bf8d2aeaf7566082260cfa15955a5a6e96e6a29935b64132f/scipy-1.16.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1fb2472e72e24d1530debe6ae078db70fb1605350c88a3d14bc401d6306dbffe", size = 36019345, upload-time = "2025-10-28T17:33:09.773Z" }, + { url = "https://files.pythonhosted.org/packages/82/31/006cbb4b648ba379a95c87262c2855cd0d09453e500937f78b30f02fa1cd/scipy-1.16.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c5192722cffe15f9329a3948c4b1db789fbb1f05c97899187dcf009b283aea70", size = 38678975, upload-time = "2025-10-28T17:33:15.809Z" }, + { url = "https://files.pythonhosted.org/packages/c2/7f/acbd28c97e990b421af7d6d6cd416358c9c293fc958b8529e0bd5d2a2a19/scipy-1.16.3-cp312-cp312-win_amd64.whl", hash = "sha256:56edc65510d1331dae01ef9b658d428e33ed48b4f77b1d51caf479a0253f96dc", size = 38555926, upload-time = "2025-10-28T17:33:21.388Z" }, + { url = "https://files.pythonhosted.org/packages/ce/69/c5c7807fd007dad4f48e0a5f2153038dc96e8725d3345b9ee31b2b7bed46/scipy-1.16.3-cp312-cp312-win_arm64.whl", hash = "sha256:a8a26c78ef223d3e30920ef759e25625a0ecdd0d60e5a8818b7513c3e5384cf2", size = 25463014, upload-time = "2025-10-28T17:33:25.975Z" }, +] + +[[package]] +name = "seaborn" +version = "0.13.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "matplotlib" }, + { 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" } +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" }, +] + +[[package]] +name = "segmentation-models-pytorch" +version = "0.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, + { name = "numpy" }, + { name = "pillow" }, + { name = "safetensors" }, + { name = "timm" }, + { name = "torch" }, + { name = "torchvision" }, + { name = "tqdm" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/15/fa/d5a29d49240fb10bdead608b4d0c6805684a8f63b1f65863502be65b1ca4/segmentation_models_pytorch-0.5.0.tar.gz", hash = "sha256:cabba8aced6ef7bdcd6288dd9e1dc2840848aa819d539c455bd07aeceb2fdf96", size = 105150, upload-time = "2025-04-17T10:43:45.755Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/62/a50e5ac6191a498ad631e54df13d7e2d7eeb1325b15ee9ea1ee3ec065aaa/segmentation_models_pytorch-0.5.0-py3-none-any.whl", hash = "sha256:c34e09047771aa4dd8878b4f899e8125700cd1f8f7db16e58c37204154151a05", size = 154789, upload-time = "2025-04-17T10:43:43.668Z" }, +] + +[[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", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scipy", version = "1.16.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { 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, upload-time = "2025-10-22T12:47:55.019Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bb/a6/a607a737dc1a00b7afe267b9bfde101b8cee2529e197e57471d23137d4e5/sentence_transformers-5.1.2-py3-none-any.whl", hash = "sha256:724ce0ea62200f413f1a5059712aff66495bc4e815a1493f7f9bca242414c333", size = 488009, upload-time = "2025-10-22T12:47:53.433Z" }, +] + +[[package]] +name = "sentry-sdk" +version = "2.46.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7c/d7/c140a5837649e2bf2ec758494fde1d9a016c76777eab64e75ef38d685bbb/sentry_sdk-2.46.0.tar.gz", hash = "sha256:91821a23460725734b7741523021601593f35731808afc0bb2ba46c27b8acd91", size = 374761, upload-time = "2025-11-24T09:34:13.932Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4b/b6/ce7c502a366f4835b1f9c057753f6989a92d3c70cbadb168193f5fb7499b/sentry_sdk-2.46.0-py2.py3-none-any.whl", hash = "sha256:4eeeb60198074dff8d066ea153fa6f241fef1668c10900ea53a4200abc8da9b1", size = 406266, upload-time = "2025-11-24T09:34:12.114Z" }, +] + +[[package]] +name = "setproctitle" +version = "1.3.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8d/48/49393a96a2eef1ab418b17475fb92b8fcfad83d099e678751b05472e69de/setproctitle-1.3.7.tar.gz", hash = "sha256:bc2bc917691c1537d5b9bca1468437176809c7e11e5694ca79a9ca12345dcb9e", size = 27002, upload-time = "2025-09-05T12:51:25.278Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f2/48/fb401ec8c4953d519d05c87feca816ad668b8258448ff60579ac7a1c1386/setproctitle-1.3.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cf555b6299f10a6eb44e4f96d2f5a3884c70ce25dc5c8796aaa2f7b40e72cb1b", size = 18079, upload-time = "2025-09-05T12:49:07.732Z" }, + { url = "https://files.pythonhosted.org/packages/cc/a3/c2b0333c2716fb3b4c9a973dd113366ac51b4f8d56b500f4f8f704b4817a/setproctitle-1.3.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:690b4776f9c15aaf1023bb07d7c5b797681a17af98a4a69e76a1d504e41108b7", size = 13099, upload-time = "2025-09-05T12:49:09.222Z" }, + { url = "https://files.pythonhosted.org/packages/0e/f8/17bda581c517678260e6541b600eeb67745f53596dc077174141ba2f6702/setproctitle-1.3.7-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:00afa6fc507967d8c9d592a887cdc6c1f5742ceac6a4354d111ca0214847732c", size = 31793, upload-time = "2025-09-05T12:49:10.297Z" }, + { url = "https://files.pythonhosted.org/packages/27/d1/76a33ae80d4e788ecab9eb9b53db03e81cfc95367ec7e3fbf4989962fedd/setproctitle-1.3.7-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9e02667f6b9fc1238ba753c0f4b0a37ae184ce8f3bbbc38e115d99646b3f4cd3", size = 32779, upload-time = "2025-09-05T12:49:12.157Z" }, + { url = "https://files.pythonhosted.org/packages/59/27/1a07c38121967061564f5e0884414a5ab11a783260450172d4fc68c15621/setproctitle-1.3.7-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:83fcd271567d133eb9532d3b067c8a75be175b2b3b271e2812921a05303a693f", size = 34578, upload-time = "2025-09-05T12:49:13.393Z" }, + { url = "https://files.pythonhosted.org/packages/d8/d4/725e6353935962d8bb12cbf7e7abba1d0d738c7f6935f90239d8e1ccf913/setproctitle-1.3.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13fe37951dda1a45c35d77d06e3da5d90e4f875c4918a7312b3b4556cfa7ff64", size = 32030, upload-time = "2025-09-05T12:49:15.362Z" }, + { url = "https://files.pythonhosted.org/packages/67/24/e4677ae8e1cb0d549ab558b12db10c175a889be0974c589c428fece5433e/setproctitle-1.3.7-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:a05509cfb2059e5d2ddff701d38e474169e9ce2a298cf1b6fd5f3a213a553fe5", size = 33363, upload-time = "2025-09-05T12:49:16.829Z" }, + { url = "https://files.pythonhosted.org/packages/55/d4/69ce66e4373a48fdbb37489f3ded476bb393e27f514968c3a69a67343ae0/setproctitle-1.3.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6da835e76ae18574859224a75db6e15c4c2aaa66d300a57efeaa4c97ca4c7381", size = 31508, upload-time = "2025-09-05T12:49:18.032Z" }, + { url = "https://files.pythonhosted.org/packages/4b/5a/42c1ed0e9665d068146a68326529b5686a1881c8b9197c2664db4baf6aeb/setproctitle-1.3.7-cp310-cp310-win32.whl", hash = "sha256:9e803d1b1e20240a93bac0bc1025363f7f80cb7eab67dfe21efc0686cc59ad7c", size = 12558, upload-time = "2025-09-05T12:49:19.742Z" }, + { url = "https://files.pythonhosted.org/packages/dc/fe/dd206cc19a25561921456f6cb12b405635319299b6f366e0bebe872abc18/setproctitle-1.3.7-cp310-cp310-win_amd64.whl", hash = "sha256:a97200acc6b64ec4cada52c2ecaf1fba1ef9429ce9c542f8a7db5bcaa9dcbd95", size = 13245, upload-time = "2025-09-05T12:49:21.023Z" }, + { url = "https://files.pythonhosted.org/packages/04/cd/1b7ba5cad635510720ce19d7122154df96a2387d2a74217be552887c93e5/setproctitle-1.3.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a600eeb4145fb0ee6c287cb82a2884bd4ec5bbb076921e287039dcc7b7cc6dd0", size = 18085, upload-time = "2025-09-05T12:49:22.183Z" }, + { url = "https://files.pythonhosted.org/packages/8f/1a/b2da0a620490aae355f9d72072ac13e901a9fec809a6a24fc6493a8f3c35/setproctitle-1.3.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:97a090fed480471bb175689859532709e28c085087e344bca45cf318034f70c4", size = 13097, upload-time = "2025-09-05T12:49:23.322Z" }, + { url = "https://files.pythonhosted.org/packages/18/2e/bd03ff02432a181c1787f6fc2a678f53b7dacdd5ded69c318fe1619556e8/setproctitle-1.3.7-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1607b963e7b53e24ec8a2cb4e0ab3ae591d7c6bf0a160feef0551da63452b37f", size = 32191, upload-time = "2025-09-05T12:49:24.567Z" }, + { url = "https://files.pythonhosted.org/packages/28/78/1e62fc0937a8549f2220445ed2175daacee9b6764c7963b16148119b016d/setproctitle-1.3.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a20fb1a3974e2dab857870cf874b325b8705605cb7e7e8bcbb915bca896f52a9", size = 33203, upload-time = "2025-09-05T12:49:25.871Z" }, + { url = "https://files.pythonhosted.org/packages/a0/3c/65edc65db3fa3df400cf13b05e9d41a3c77517b4839ce873aa6b4043184f/setproctitle-1.3.7-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f8d961bba676e07d77665204f36cffaa260f526e7b32d07ab3df6a2c1dfb44ba", size = 34963, upload-time = "2025-09-05T12:49:27.044Z" }, + { url = "https://files.pythonhosted.org/packages/a1/32/89157e3de997973e306e44152522385f428e16f92f3cf113461489e1e2ee/setproctitle-1.3.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:db0fd964fbd3a9f8999b502f65bd2e20883fdb5b1fae3a424e66db9a793ed307", size = 32398, upload-time = "2025-09-05T12:49:28.909Z" }, + { url = "https://files.pythonhosted.org/packages/4a/18/77a765a339ddf046844cb4513353d8e9dcd8183da9cdba6e078713e6b0b2/setproctitle-1.3.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:db116850fcf7cca19492030f8d3b4b6e231278e8fe097a043957d22ce1bdf3ee", size = 33657, upload-time = "2025-09-05T12:49:30.323Z" }, + { url = "https://files.pythonhosted.org/packages/6b/63/f0b6205c64d74d2a24a58644a38ec77bdbaa6afc13747e75973bf8904932/setproctitle-1.3.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:316664d8b24a5c91ee244460bdaf7a74a707adaa9e14fbe0dc0a53168bb9aba1", size = 31836, upload-time = "2025-09-05T12:49:32.309Z" }, + { url = "https://files.pythonhosted.org/packages/ba/51/e1277f9ba302f1a250bbd3eedbbee747a244b3cc682eb58fb9733968f6d8/setproctitle-1.3.7-cp311-cp311-win32.whl", hash = "sha256:b74774ca471c86c09b9d5037c8451fff06bb82cd320d26ae5a01c758088c0d5d", size = 12556, upload-time = "2025-09-05T12:49:33.529Z" }, + { url = "https://files.pythonhosted.org/packages/b6/7b/822a23f17e9003dfdee92cd72758441ca2a3680388da813a371b716fb07f/setproctitle-1.3.7-cp311-cp311-win_amd64.whl", hash = "sha256:acb9097213a8dd3410ed9f0dc147840e45ca9797785272928d4be3f0e69e3be4", size = 13243, upload-time = "2025-09-05T12:49:34.553Z" }, + { url = "https://files.pythonhosted.org/packages/fb/f0/2dc88e842077719d7384d86cc47403e5102810492b33680e7dadcee64cd8/setproctitle-1.3.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2dc99aec591ab6126e636b11035a70991bc1ab7a261da428491a40b84376654e", size = 18049, upload-time = "2025-09-05T12:49:36.241Z" }, + { url = "https://files.pythonhosted.org/packages/f0/b4/50940504466689cda65680c9e9a1e518e5750c10490639fa687489ac7013/setproctitle-1.3.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cdd8aa571b7aa39840fdbea620e308a19691ff595c3a10231e9ee830339dd798", size = 13079, upload-time = "2025-09-05T12:49:38.088Z" }, + { url = "https://files.pythonhosted.org/packages/d0/99/71630546b9395b095f4082be41165d1078204d1696c2d9baade3de3202d0/setproctitle-1.3.7-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2906b6c7959cdb75f46159bf0acd8cc9906cf1361c9e1ded0d065fe8f9039629", size = 32932, upload-time = "2025-09-05T12:49:39.271Z" }, + { url = "https://files.pythonhosted.org/packages/50/22/cee06af4ffcfb0e8aba047bd44f5262e644199ae7527ae2c1f672b86495c/setproctitle-1.3.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6915964a6dda07920a1159321dcd6d94fc7fc526f815ca08a8063aeca3c204f1", size = 33736, upload-time = "2025-09-05T12:49:40.565Z" }, + { url = "https://files.pythonhosted.org/packages/5c/00/a5949a8bb06ef5e7df214fc393bb2fb6aedf0479b17214e57750dfdd0f24/setproctitle-1.3.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cff72899861c765bd4021d1ff1c68d60edc129711a2fdba77f9cb69ef726a8b6", size = 35605, upload-time = "2025-09-05T12:49:42.362Z" }, + { url = "https://files.pythonhosted.org/packages/b0/3a/50caca532a9343828e3bf5778c7a84d6c737a249b1796d50dd680290594d/setproctitle-1.3.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b7cb05bd446687ff816a3aaaf831047fc4c364feff7ada94a66024f1367b448c", size = 33143, upload-time = "2025-09-05T12:49:43.515Z" }, + { url = "https://files.pythonhosted.org/packages/ca/14/b843a251296ce55e2e17c017d6b9f11ce0d3d070e9265de4ecad948b913d/setproctitle-1.3.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:3a57b9a00de8cae7e2a1f7b9f0c2ac7b69372159e16a7708aa2f38f9e5cc987a", size = 34434, upload-time = "2025-09-05T12:49:45.31Z" }, + { url = "https://files.pythonhosted.org/packages/c8/b7/06145c238c0a6d2c4bc881f8be230bb9f36d2bf51aff7bddcb796d5eed67/setproctitle-1.3.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d8828b356114f6b308b04afe398ed93803d7fca4a955dd3abe84430e28d33739", size = 32795, upload-time = "2025-09-05T12:49:46.419Z" }, + { url = "https://files.pythonhosted.org/packages/ef/dc/ef76a81fac9bf27b84ed23df19c1f67391a753eed6e3c2254ebcb5133f56/setproctitle-1.3.7-cp312-cp312-win32.whl", hash = "sha256:b0304f905efc845829ac2bc791ddebb976db2885f6171f4a3de678d7ee3f7c9f", size = 12552, upload-time = "2025-09-05T12:49:47.635Z" }, + { url = "https://files.pythonhosted.org/packages/e2/5b/a9fe517912cd6e28cf43a212b80cb679ff179a91b623138a99796d7d18a0/setproctitle-1.3.7-cp312-cp312-win_amd64.whl", hash = "sha256:9888ceb4faea3116cf02a920ff00bfbc8cc899743e4b4ac914b03625bdc3c300", size = 13247, upload-time = "2025-09-05T12:49:49.16Z" }, + { url = "https://files.pythonhosted.org/packages/34/8a/aff5506ce89bc3168cb492b18ba45573158d528184e8a9759a05a09088a9/setproctitle-1.3.7-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:eb440c5644a448e6203935ed60466ec8d0df7278cd22dc6cf782d07911bcbea6", size = 12654, upload-time = "2025-09-05T12:51:17.141Z" }, + { url = "https://files.pythonhosted.org/packages/41/89/5b6f2faedd6ced3d3c085a5efbd91380fb1f61f4c12bc42acad37932f4e9/setproctitle-1.3.7-pp310-pypy310_pp73-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:502b902a0e4c69031b87870ff4986c290ebbb12d6038a70639f09c331b18efb2", size = 14284, upload-time = "2025-09-05T12:51:18.393Z" }, + { url = "https://files.pythonhosted.org/packages/0a/c0/4312fed3ca393a29589603fd48f17937b4ed0638b923bac75a728382e730/setproctitle-1.3.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:f6f268caeabb37ccd824d749e7ce0ec6337c4ed954adba33ec0d90cc46b0ab78", size = 13282, upload-time = "2025-09-05T12:51:19.703Z" }, + { url = "https://files.pythonhosted.org/packages/c3/5b/5e1c117ac84e3cefcf8d7a7f6b2461795a87e20869da065a5c087149060b/setproctitle-1.3.7-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:b1cac6a4b0252b8811d60b6d8d0f157c0fdfed379ac89c25a914e6346cf355a1", size = 12587, upload-time = "2025-09-05T12:51:21.195Z" }, + { url = "https://files.pythonhosted.org/packages/73/02/b9eadc226195dcfa90eed37afe56b5dd6fa2f0e5220ab8b7867b8862b926/setproctitle-1.3.7-pp311-pypy311_pp73-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f1704c9e041f2b1dc38f5be4552e141e1432fba3dd52c72eeffd5bc2db04dc65", size = 14286, upload-time = "2025-09-05T12:51:22.61Z" }, + { url = "https://files.pythonhosted.org/packages/28/26/1be1d2a53c2a91ec48fa2ff4a409b395f836798adf194d99de9c059419ea/setproctitle-1.3.7-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:b08b61976ffa548bd5349ce54404bf6b2d51bd74d4f1b241ed1b0f25bce09c3a", size = 13282, upload-time = "2025-09-05T12:51:24.094Z" }, +] + +[[package]] +name = "setuptools" +version = "70.2.0" +source = { registry = "https://download.pytorch.org/whl/cu126" } +wheels = [ + { url = "https://download.pytorch.org/whl/setuptools-70.2.0-py3-none-any.whl", hash = "sha256:b8b8060bb426838fbe942479c90296ce976249451118ef566a5a0b7d8b78fb05" }, +] + +[[package]] +name = "shap" +version = "0.47.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cloudpickle" }, + { name = "numba" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pandas" }, + { name = "scikit-learn" }, + { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scipy", version = "1.16.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "slicer" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/33/7d/ffcdb3f245dba816e3c1de87549f1fc3067346c95219e4fcd91fc4bc7962/shap-0.47.1.tar.gz", hash = "sha256:292dc548c53fb0438fddacea793eb3482936621ad50a65529630684155625dce", size = 2501647, upload-time = "2025-03-22T21:05:31.736Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d8/3d/9431cfdc2fbe8a742f1bb4f26e7641f6423eb608f291962ef11c237a2a11/shap-0.47.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:65d68e714b1781808db1018b81dbec2745eba70d6ea3575c1e21583a941a5cf9", size = 498981, upload-time = "2025-03-22T21:04:49.319Z" }, + { url = "https://files.pythonhosted.org/packages/14/a2/6fe9564710a5c7c7749c2e1a71e65ddac934fde9ed2e540ea57179a10b21/shap-0.47.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7995d7311457032c990149002bb32224b0c0fb674ade264f348b415b4509fd39", size = 491933, upload-time = "2025-03-22T21:04:51.671Z" }, + { url = "https://files.pythonhosted.org/packages/9d/a0/e75cc918430896febc3d4548af7511afc375a51288fd0cb22f64fa4a26f5/shap-0.47.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05d8082ffed7de29bd1c8aec9fd35432145f8f817b3aa90fa0a43c7de1b3078c", size = 930773, upload-time = "2025-03-22T21:04:53.643Z" }, + { url = "https://files.pythonhosted.org/packages/96/5d/858bd4ee7fc655658e207b31e4e601bb4b609e344615696879f715c7485d/shap-0.47.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:224453a8fd089c1866aba90856ed14ac125a08a1c5ae8a4f40c4c1192a48843c", size = 937665, upload-time = "2025-03-22T21:04:55.456Z" }, + { url = "https://files.pythonhosted.org/packages/a6/5f/5a014ad44b90d45c2c6f2826df0bf5260b88a501ba89af8f3e94727a4139/shap-0.47.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ed48a54fd3f9faa722ecf912453f5d833cb4289fe2360d5d6c5a3498bcb275b1", size = 1952978, upload-time = "2025-03-22T21:04:57.595Z" }, + { url = "https://files.pythonhosted.org/packages/28/24/9aad1bc4487e812482786d6ae4eb7bf0015b26bb8fd08306e17762696940/shap-0.47.1-cp310-cp310-win_amd64.whl", hash = "sha256:61d4fd953805961d212f6c5fd79c713c24cd24769e0cbc1926868b26c0183196", size = 489629, upload-time = "2025-03-22T21:04:59.455Z" }, + { url = "https://files.pythonhosted.org/packages/c3/65/ccad788e04eff97de1afb0e3c46fedbb0690bfc1366aa0291bc6454b3550/shap-0.47.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6afca56a634190133710f65c6618c942083d3533e6d2463bcd65317e12587c4d", size = 498900, upload-time = "2025-03-22T21:05:01.15Z" }, + { url = "https://files.pythonhosted.org/packages/58/88/5a6649b5b5336489d3d87246ac95420aff2bb30a9f90c8875c84a82740c7/shap-0.47.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3275d45fafced93978ffeee5aeb035637e54646df5d86624b7d3c8317c77e5f7", size = 491789, upload-time = "2025-03-22T21:05:02.877Z" }, + { url = "https://files.pythonhosted.org/packages/97/b1/073fe5ea0d3aa78eeb0fde56bb4f0ccf403d41c1f8661ef37607c0270299/shap-0.47.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5b9b39ef79a2e683ff11c54191aeb75ef4553e29ff4f3d4219a2bd8ddaf071b", size = 965119, upload-time = "2025-03-22T21:05:04.842Z" }, + { url = "https://files.pythonhosted.org/packages/2a/c2/aa91dbb9cc8eee20f5bd245fd8fe27fc45fba786c3e04d35ed510f111b17/shap-0.47.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8acf428a8af28576d58770bdd81bf33bb82aaa00495f821e5dbf4b0d1028d74", size = 972561, upload-time = "2025-03-22T21:05:06.439Z" }, + { url = "https://files.pythonhosted.org/packages/d7/a3/73036cf72cce268ad4d338d648e3ec1e0f035fe047a48bb634f114f0167a/shap-0.47.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4789ef942e7f57aa9810497c1fdd0fba0011a9fce91010802ff39cfac4bd8921", size = 1990476, upload-time = "2025-03-22T21:05:08.704Z" }, + { url = "https://files.pythonhosted.org/packages/72/28/d4dcf1b06761c916f642e0fe1293ed8081ce32991b32eada2212f639433c/shap-0.47.1-cp311-cp311-win_amd64.whl", hash = "sha256:673904d6219b1ef65f1d2fb311357de4e56d0fdeb062a6853ba11d57f4305ef2", size = 489822, upload-time = "2025-03-22T21:05:10.734Z" }, + { url = "https://files.pythonhosted.org/packages/a7/38/2122dc1ea73b8e152d5547f547a9574f76dc298351c06360d1e0da97f5f3/shap-0.47.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:961fd025aa5d53b6df947ab0e2d6b1f98d206bac36ed65c4c51f74921b7dadbc", size = 500293, upload-time = "2025-03-22T21:05:12.51Z" }, + { url = "https://files.pythonhosted.org/packages/8a/4f/071cc7b54566765269d4c73a2de8b64b43a1b05f978b66d9a04a8b73ace0/shap-0.47.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0f876a4eb06666555f5b83edca3c900ca7c3a8d5ebf2deef330b41a62f7dda7f", size = 492315, upload-time = "2025-03-22T21:05:13.958Z" }, + { url = "https://files.pythonhosted.org/packages/01/d8/eec1414b04a21cec3b27834c505de8be861a49eb583550f653d5a4848f26/shap-0.47.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f67da47d287dcd861dd664d92d595c8a0d5c89cba895e37c21b76ca905edc72", size = 957359, upload-time = "2025-03-22T21:05:15.384Z" }, + { url = "https://files.pythonhosted.org/packages/41/33/ca5f9dd2e2b6ddf88892f787cfb8fb3ab539488901428a189c14d1ef03b7/shap-0.47.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0a36cdc8c681901491714d9cea9f98af4c2da6328394382195386f99d520b5e", size = 967307, upload-time = "2025-03-22T21:05:17.038Z" }, + { url = "https://files.pythonhosted.org/packages/43/fe/be106ba63beea861faee8a2ae7a6c18704a44784e0f6466a9ef18d7cf0b0/shap-0.47.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:55709c08d163b74e9de4b201e8fe37362270914cef43e89c75d58728f1c5f0b6", size = 1985261, upload-time = "2025-03-22T21:05:19.092Z" }, + { url = "https://files.pythonhosted.org/packages/1c/d8/83094be435583d2ed8d50fe2280497883b767fe19350bfc0165f4a43a053/shap-0.47.1-cp312-cp312-win_amd64.whl", hash = "sha256:4dd5a497dba744e1190fbc1631603f5d8441eabde0bf882b9e6aefb9f083812d", size = 490601, upload-time = "2025-03-22T21:05:20.705Z" }, +] + +[[package]] +name = "simplejson" +version = "3.20.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/41/f4/a1ac5ed32f7ed9a088d62a59d410d4c204b3b3815722e2ccfb491fa8251b/simplejson-3.20.2.tar.gz", hash = "sha256:5fe7a6ce14d1c300d80d08695b7f7e633de6cd72c80644021874d985b3393649", size = 85784, upload-time = "2025-09-26T16:29:36.64Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/09/2bf3761de89ea2d91bdce6cf107dcd858892d0adc22c995684878826cc6b/simplejson-3.20.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6d7286dc11af60a2f76eafb0c2acde2d997e87890e37e24590bb513bec9f1bc5", size = 94039, upload-time = "2025-09-26T16:27:29.283Z" }, + { url = "https://files.pythonhosted.org/packages/0f/33/c3277db8931f0ae9e54b9292668863365672d90fb0f632f4cf9829cb7d68/simplejson-3.20.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c01379b4861c3b0aa40cba8d44f2b448f5743999aa68aaa5d3ef7049d4a28a2d", size = 75894, upload-time = "2025-09-26T16:27:30.378Z" }, + { url = "https://files.pythonhosted.org/packages/fa/ea/ae47b04d03c7c8a7b7b1a8b39a6e27c3bd424e52f4988d70aca6293ff5e5/simplejson-3.20.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a16b029ca25645b3bc44e84a4f941efa51bf93c180b31bd704ce6349d1fc77c1", size = 76116, upload-time = "2025-09-26T16:27:31.42Z" }, + { url = "https://files.pythonhosted.org/packages/4b/42/6c9af551e5a8d0f171d6dce3d9d1260068927f7b80f1f09834e07887c8c4/simplejson-3.20.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e22a5fb7b1437ffb057e02e1936a3bfb19084ae9d221ec5e9f4cf85f69946b6", size = 138827, upload-time = "2025-09-26T16:27:32.486Z" }, + { url = "https://files.pythonhosted.org/packages/2b/22/5e268bbcbe9f75577491e406ec0a5536f5b2fa91a3b52031fea51cd83e1d/simplejson-3.20.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d8b6ff02fc7b8555c906c24735908854819b0d0dc85883d453e23ca4c0445d01", size = 146772, upload-time = "2025-09-26T16:27:34.036Z" }, + { url = "https://files.pythonhosted.org/packages/71/b4/800f14728e2ad666f420dfdb57697ca128aeae7f991b35759c09356b829a/simplejson-3.20.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2bfc1c396ad972ba4431130b42307b2321dba14d988580c1ac421ec6a6b7cee3", size = 134497, upload-time = "2025-09-26T16:27:35.211Z" }, + { url = "https://files.pythonhosted.org/packages/c1/b9/c54eef4226c6ac8e9a389bbe5b21fef116768f97a2dc1a683c716ffe66ef/simplejson-3.20.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a97249ee1aee005d891b5a211faf58092a309f3d9d440bc269043b08f662eda", size = 138172, upload-time = "2025-09-26T16:27:36.44Z" }, + { url = "https://files.pythonhosted.org/packages/09/36/4e282f5211b34620f1b2e4b51d9ddaab5af82219b9b7b78360a33f7e5387/simplejson-3.20.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f1036be00b5edaddbddbb89c0f80ed229714a941cfd21e51386dc69c237201c2", size = 140272, upload-time = "2025-09-26T16:27:37.605Z" }, + { url = "https://files.pythonhosted.org/packages/aa/b0/94ad2cf32f477c449e1f63c863d8a513e2408d651c4e58fe4b6a7434e168/simplejson-3.20.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5d6f5bacb8cdee64946b45f2680afa3f54cd38e62471ceda89f777693aeca4e4", size = 140468, upload-time = "2025-09-26T16:27:39.015Z" }, + { url = "https://files.pythonhosted.org/packages/e5/46/827731e4163be3f987cb8ee90f5d444161db8f540b5e735355faa098d9bc/simplejson-3.20.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8db6841fb796ec5af632f677abf21c6425a1ebea0d9ac3ef1a340b8dc69f52b8", size = 148700, upload-time = "2025-09-26T16:27:40.171Z" }, + { url = "https://files.pythonhosted.org/packages/c7/28/c32121064b1ec2fb7b5d872d9a1abda62df064d35e0160eddfa907118343/simplejson-3.20.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c0a341f7cc2aae82ee2b31f8a827fd2e51d09626f8b3accc441a6907c88aedb7", size = 141323, upload-time = "2025-09-26T16:27:41.324Z" }, + { url = "https://files.pythonhosted.org/packages/46/b6/c897c54326fe86dd12d101981171a49361949f4728294f418c3b86a1af77/simplejson-3.20.2-cp310-cp310-win32.whl", hash = "sha256:27f9c01a6bc581d32ab026f515226864576da05ef322d7fc141cd8a15a95ce53", size = 74377, upload-time = "2025-09-26T16:27:42.533Z" }, + { url = "https://files.pythonhosted.org/packages/ad/87/a6e03d4d80cca99c1fee4e960f3440e2f21be9470e537970f960ca5547f1/simplejson-3.20.2-cp310-cp310-win_amd64.whl", hash = "sha256:c0a63ec98a4547ff366871bf832a7367ee43d047bcec0b07b66c794e2137b476", size = 76081, upload-time = "2025-09-26T16:27:43.945Z" }, + { url = "https://files.pythonhosted.org/packages/b9/3e/96898c6c66d9dca3f9bd14d7487bf783b4acc77471b42f979babbb68d4ca/simplejson-3.20.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:06190b33cd7849efc413a5738d3da00b90e4a5382fd3d584c841ac20fb828c6f", size = 92633, upload-time = "2025-09-26T16:27:45.028Z" }, + { url = "https://files.pythonhosted.org/packages/6b/a2/cd2e10b880368305d89dd540685b8bdcc136df2b3c76b5ddd72596254539/simplejson-3.20.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4ad4eac7d858947a30d2c404e61f16b84d16be79eb6fb316341885bdde864fa8", size = 75309, upload-time = "2025-09-26T16:27:46.142Z" }, + { url = "https://files.pythonhosted.org/packages/5d/02/290f7282eaa6ebe945d35c47e6534348af97472446951dce0d144e013f4c/simplejson-3.20.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b392e11c6165d4a0fde41754a0e13e1d88a5ad782b245a973dd4b2bdb4e5076a", size = 75308, upload-time = "2025-09-26T16:27:47.542Z" }, + { url = "https://files.pythonhosted.org/packages/43/91/43695f17b69e70c4b0b03247aa47fb3989d338a70c4b726bbdc2da184160/simplejson-3.20.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:51eccc4e353eed3c50e0ea2326173acdc05e58f0c110405920b989d481287e51", size = 143733, upload-time = "2025-09-26T16:27:48.673Z" }, + { url = "https://files.pythonhosted.org/packages/9b/4b/fdcaf444ac1c3cbf1c52bf00320c499e1cf05d373a58a3731ae627ba5e2d/simplejson-3.20.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:306e83d7c331ad833d2d43c76a67f476c4b80c4a13334f6e34bb110e6105b3bd", size = 153397, upload-time = "2025-09-26T16:27:49.89Z" }, + { url = "https://files.pythonhosted.org/packages/c4/83/21550f81a50cd03599f048a2d588ffb7f4c4d8064ae091511e8e5848eeaa/simplejson-3.20.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f820a6ac2ef0bc338ae4963f4f82ccebdb0824fe9caf6d660670c578abe01013", size = 141654, upload-time = "2025-09-26T16:27:51.168Z" }, + { url = "https://files.pythonhosted.org/packages/cf/54/d76c0e72ad02450a3e723b65b04f49001d0e73218ef6a220b158a64639cb/simplejson-3.20.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21e7a066528a5451433eb3418184f05682ea0493d14e9aae690499b7e1eb6b81", size = 144913, upload-time = "2025-09-26T16:27:52.331Z" }, + { url = "https://files.pythonhosted.org/packages/3f/49/976f59b42a6956d4aeb075ada16ad64448a985704bc69cd427a2245ce835/simplejson-3.20.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:438680ddde57ea87161a4824e8de04387b328ad51cfdf1eaf723623a3014b7aa", size = 144568, upload-time = "2025-09-26T16:27:53.41Z" }, + { url = "https://files.pythonhosted.org/packages/60/c7/30bae30424ace8cd791ca660fed454ed9479233810fe25c3f3eab3d9dc7b/simplejson-3.20.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:cac78470ae68b8d8c41b6fca97f5bf8e024ca80d5878c7724e024540f5cdaadb", size = 146239, upload-time = "2025-09-26T16:27:54.502Z" }, + { url = "https://files.pythonhosted.org/packages/79/3e/7f3b7b97351c53746e7b996fcd106986cda1954ab556fd665314756618d2/simplejson-3.20.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:7524e19c2da5ef281860a3d74668050c6986be15c9dd99966034ba47c68828c2", size = 154497, upload-time = "2025-09-26T16:27:55.885Z" }, + { url = "https://files.pythonhosted.org/packages/1d/48/7241daa91d0bf19126589f6a8dcbe8287f4ed3d734e76fd4a092708947be/simplejson-3.20.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0e9b6d845a603b2eef3394eb5e21edb8626cd9ae9a8361d14e267eb969dbe413", size = 148069, upload-time = "2025-09-26T16:27:57.039Z" }, + { url = "https://files.pythonhosted.org/packages/e6/f4/ef18d2962fe53e7be5123d3784e623859eec7ed97060c9c8536c69d34836/simplejson-3.20.2-cp311-cp311-win32.whl", hash = "sha256:47d8927e5ac927fdd34c99cc617938abb3624b06ff86e8e219740a86507eb961", size = 74158, upload-time = "2025-09-26T16:27:58.265Z" }, + { url = "https://files.pythonhosted.org/packages/35/fd/3d1158ecdc573fdad81bf3cc78df04522bf3959758bba6597ba4c956c74d/simplejson-3.20.2-cp311-cp311-win_amd64.whl", hash = "sha256:ba4edf3be8e97e4713d06c3d302cba1ff5c49d16e9d24c209884ac1b8455520c", size = 75911, upload-time = "2025-09-26T16:27:59.292Z" }, + { url = "https://files.pythonhosted.org/packages/9d/9e/1a91e7614db0416885eab4136d49b7303de20528860ffdd798ce04d054db/simplejson-3.20.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:4376d5acae0d1e91e78baeba4ee3cf22fbf6509d81539d01b94e0951d28ec2b6", size = 93523, upload-time = "2025-09-26T16:28:00.356Z" }, + { url = "https://files.pythonhosted.org/packages/5e/2b/d2413f5218fc25608739e3d63fe321dfa85c5f097aa6648dbe72513a5f12/simplejson-3.20.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f8fe6de652fcddae6dec8f281cc1e77e4e8f3575249e1800090aab48f73b4259", size = 75844, upload-time = "2025-09-26T16:28:01.756Z" }, + { url = "https://files.pythonhosted.org/packages/ad/f1/efd09efcc1e26629e120fef59be059ce7841cc6e1f949a4db94f1ae8a918/simplejson-3.20.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:25ca2663d99328d51e5a138f22018e54c9162438d831e26cfc3458688616eca8", size = 75655, upload-time = "2025-09-26T16:28:03.037Z" }, + { url = "https://files.pythonhosted.org/packages/97/ec/5c6db08e42f380f005d03944be1af1a6bd501cc641175429a1cbe7fb23b9/simplejson-3.20.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:12a6b2816b6cab6c3fd273d43b1948bc9acf708272074c8858f579c394f4cbc9", size = 150335, upload-time = "2025-09-26T16:28:05.027Z" }, + { url = "https://files.pythonhosted.org/packages/81/f5/808a907485876a9242ec67054da7cbebefe0ee1522ef1c0be3bfc90f96f6/simplejson-3.20.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac20dc3fcdfc7b8415bfc3d7d51beccd8695c3f4acb7f74e3a3b538e76672868", size = 158519, upload-time = "2025-09-26T16:28:06.5Z" }, + { url = "https://files.pythonhosted.org/packages/66/af/b8a158246834645ea890c36136584b0cc1c0e4b83a73b11ebd9c2a12877c/simplejson-3.20.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db0804d04564e70862ef807f3e1ace2cc212ef0e22deb1b3d6f80c45e5882c6b", size = 148571, upload-time = "2025-09-26T16:28:07.715Z" }, + { url = "https://files.pythonhosted.org/packages/20/05/ed9b2571bbf38f1a2425391f18e3ac11cb1e91482c22d644a1640dea9da7/simplejson-3.20.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:979ce23ea663895ae39106946ef3d78527822d918a136dbc77b9e2b7f006237e", size = 152367, upload-time = "2025-09-26T16:28:08.921Z" }, + { url = "https://files.pythonhosted.org/packages/81/2c/bad68b05dd43e93f77994b920505634d31ed239418eb6a88997d06599983/simplejson-3.20.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a2ba921b047bb029805726800819675249ef25d2f65fd0edb90639c5b1c3033c", size = 150205, upload-time = "2025-09-26T16:28:10.086Z" }, + { url = "https://files.pythonhosted.org/packages/69/46/90c7fc878061adafcf298ce60cecdee17a027486e9dce507e87396d68255/simplejson-3.20.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:12d3d4dc33770069b780cc8f5abef909fe4a3f071f18f55f6d896a370fd0f970", size = 151823, upload-time = "2025-09-26T16:28:11.329Z" }, + { url = "https://files.pythonhosted.org/packages/ab/27/b85b03349f825ae0f5d4f780cdde0bbccd4f06c3d8433f6a3882df887481/simplejson-3.20.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:aff032a59a201b3683a34be1169e71ddda683d9c3b43b261599c12055349251e", size = 158997, upload-time = "2025-09-26T16:28:12.917Z" }, + { url = "https://files.pythonhosted.org/packages/71/ad/d7f3c331fb930638420ac6d236db68e9f4c28dab9c03164c3cd0e7967e15/simplejson-3.20.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:30e590e133b06773f0dc9c3f82e567463df40598b660b5adf53eb1c488202544", size = 154367, upload-time = "2025-09-26T16:28:14.393Z" }, + { url = "https://files.pythonhosted.org/packages/f0/46/5c67324addd40fa2966f6e886cacbbe0407c03a500db94fb8bb40333fcdf/simplejson-3.20.2-cp312-cp312-win32.whl", hash = "sha256:8d7be7c99939cc58e7c5bcf6bb52a842a58e6c65e1e9cdd2a94b697b24cddb54", size = 74285, upload-time = "2025-09-26T16:28:15.931Z" }, + { url = "https://files.pythonhosted.org/packages/fa/c9/5cc2189f4acd3a6e30ffa9775bf09b354302dbebab713ca914d7134d0f29/simplejson-3.20.2-cp312-cp312-win_amd64.whl", hash = "sha256:2c0b4a67e75b945489052af6590e7dca0ed473ead5d0f3aad61fa584afe814ab", size = 75969, upload-time = "2025-09-26T16:28:17.017Z" }, + { url = "https://files.pythonhosted.org/packages/05/5b/83e1ff87eb60ca706972f7e02e15c0b33396e7bdbd080069a5d1b53cf0d8/simplejson-3.20.2-py3-none-any.whl", hash = "sha256:3b6bb7fb96efd673eac2e4235200bfffdc2353ad12c54117e1e4e2fc485ac017", size = 57309, upload-time = "2025-09-26T16:29:35.312Z" }, +] + +[[package]] +name = "simsimd" +version = "6.5.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/df/2f/5a9ccc385f4d6e30aac2b843ef57ba3668ea86756f77f6a9312a3c94f43d/simsimd-6.5.3.tar.gz", hash = "sha256:5ff341e84fe1c46e7268ee9e31f885936b29c38ce59f423433aef5f4bb5bfd18", size = 184865, upload-time = "2025-09-06T16:17:44.761Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/4f/308f16168d784c29a05f0a5f86a5763ec2ab2d1da9029b632eccd10e9bbd/simsimd-6.5.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:769696d4ca5de461275fe75c82d255ec4e5ffab502cf1e6b8d641508327e2f01", size = 179121, upload-time = "2025-09-06T16:14:50.57Z" }, + { url = "https://files.pythonhosted.org/packages/26/14/f2a72c689285738012bca78b8feff8a4e21a7bda9621188efb8fa121eb4d/simsimd-6.5.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ac439ba9fc08dce8bc8cb8dcf78ddd933f74a59aa9037bb5e7d5c1c6254cf28", size = 134157, upload-time = "2025-09-06T16:14:53.588Z" }, + { url = "https://files.pythonhosted.org/packages/8c/6d/34ae09ff3045a0287ca01bcd5eedafce05f01fe6c8233d28091846ce25ba/simsimd-6.5.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7f1545fc97fa32b2af081bbc9841d86025c4f6a623fc084d6dc7af6c138b1fa1", size = 562475, upload-time = "2025-09-06T16:14:54.911Z" }, + { url = "https://files.pythonhosted.org/packages/65/c3/f9cf30016eae80e90a795ffbc089576c47b7e0d6ae1a71e2384df34a5a80/simsimd-6.5.3-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:2bd844a68ea1cbe8905a80b724648613e61addf236a635339ea06dee0bae73c2", size = 355106, upload-time = "2025-09-06T16:14:56.539Z" }, + { url = "https://files.pythonhosted.org/packages/2c/9e/2f672cbc693eb9488fdd8a30ee3f006602fc51011be8cc30d43529d7ff72/simsimd-6.5.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9bd8cb1eeb0982363037202d76305fd6df88d86f02ca38fea10b1c69716d6cec", size = 410903, upload-time = "2025-09-06T16:14:58.016Z" }, + { url = "https://files.pythonhosted.org/packages/47/b5/89ddc3c156c391fc22912c1a9e64a36b8781fb1d45a0c2bac57960d48b50/simsimd-6.5.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c9aba7081452e66db9c484778c969c294006b9aebf59143344e559c3a7254e65", size = 367470, upload-time = "2025-09-06T16:14:59.83Z" }, + { url = "https://files.pythonhosted.org/packages/02/85/51e769e3ff00acf7376e58328fb62e980f1dddef9b9189cd4ea8f15264ba/simsimd-6.5.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05f64148d59ec5e6caaadcfc77284fa4187f0686cee3095d9dd9c0366b59e077", size = 1067794, upload-time = "2025-09-06T16:15:01.743Z" }, + { url = "https://files.pythonhosted.org/packages/60/49/024035a870b01e27b9c0d8fff489f677d027b80b89d75b4262d15473170d/simsimd-6.5.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:68b1924f60143ef5cf40ae38d75330e5b3c4e9953c878c1a60e913004c38d7d8", size = 597776, upload-time = "2025-09-06T16:15:03.679Z" }, + { url = "https://files.pythonhosted.org/packages/ee/54/4ff042ba49e3d8e554650df5591637c4a99acc768d916070e54bb7841444/simsimd-6.5.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:24126bb1819b5687f208c8e4d549029019387377e74eb1699ac1346b358997b6", size = 401740, upload-time = "2025-09-06T16:15:05.59Z" }, + { url = "https://files.pythonhosted.org/packages/28/58/214f1efc8c8def81ba73bb346587f5a1c02a1dda293fc8b5814d50570885/simsimd-6.5.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e94a47db1e1e18c98ead6671827662bc9a181e672573693fc281b3b2169a2e4d", size = 460558, upload-time = "2025-09-06T16:15:07.632Z" }, + { url = "https://files.pythonhosted.org/packages/0d/44/7920aa3d6d2874edc49de1bbd89f309a1d34a80f9519ff32d3d4a1fe497b/simsimd-6.5.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:17472f64eb0f7e0ee56c7865134b37f1dfb102bba6b9b92ac2c8ead8edf3dd0e", size = 371966, upload-time = "2025-09-06T16:15:09.493Z" }, + { url = "https://files.pythonhosted.org/packages/c5/b1/747a24b7a1d2b3ae2a651e1688a447f2af8a42aafd27685b173ad4815f41/simsimd-6.5.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc3c217c9912942644db64074a7745d7470273f69acc962f36ef584e88010087", size = 1000807, upload-time = "2025-09-06T16:15:10.955Z" }, + { url = "https://files.pythonhosted.org/packages/e9/2c/6e66858a6150351e74238357d3b01718f743b405f79a16b9184f07f91748/simsimd-6.5.3-cp310-cp310-win_amd64.whl", hash = "sha256:2bb463ebf97d95bfb192ede0c6e16e3db2d2a5876a74a8d593b62cecb3195765", size = 94572, upload-time = "2025-09-06T16:15:12.398Z" }, + { url = "https://files.pythonhosted.org/packages/0f/b9/e54d439e151c3511cb0941838cafefaca5b505e79acdd6260229c866a6cf/simsimd-6.5.3-cp310-cp310-win_arm64.whl", hash = "sha256:aa180116a50310dc5424df07b76dec8f745bd70024b0406816710b9f9a46ae46", size = 59378, upload-time = "2025-09-06T16:15:13.793Z" }, + { url = "https://files.pythonhosted.org/packages/b9/dc/17325531c8a54512b1b10fafbe76768608e5adc50264b9a729d53dd38c86/simsimd-6.5.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:33b64b748feb6a3f64bff8e885daf5dcc9b42678f024827e43b448aa914eefe7", size = 179121, upload-time = "2025-09-06T16:15:15.261Z" }, + { url = "https://files.pythonhosted.org/packages/cb/31/c50e0882d5748c082dc0790bc78ab84e051a68182bdc0ab624d529438ba9/simsimd-6.5.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c954adf533036dc2131fa131557317bc874f54891e7b681d0af6dba18dffa82e", size = 134156, upload-time = "2025-09-06T16:15:17.203Z" }, + { url = "https://files.pythonhosted.org/packages/73/09/132efc6d1eb778b19a53ae896c85b109ad5f8697f6e78916a29437b3ec0c/simsimd-6.5.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:05418b8d1b75f34208ff117dbcf3c62cefa3abab1a3958bcce60f43881138777", size = 563547, upload-time = "2025-09-06T16:15:18.513Z" }, + { url = "https://files.pythonhosted.org/packages/cb/d9/ea094c95d90bf6420f637cf25abd846011772d931382569bf9ae92bcd04c/simsimd-6.5.3-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:ea50a7c00b1b32100372504970118a343f57421f7ed9c0db4a362fb74d28ab7e", size = 355879, upload-time = "2025-09-06T16:15:20.026Z" }, + { url = "https://files.pythonhosted.org/packages/c3/50/85982084ff5161ffa3ff8c5bcb7bb55452c5a95db9ec671be14bfde61944/simsimd-6.5.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1b3e1bb1b91d8771ad905e90b4f06a6a7468fcd1fa8626e297816b349d6b6182", size = 411791, upload-time = "2025-09-06T16:15:21.822Z" }, + { url = "https://files.pythonhosted.org/packages/68/41/b83c0fe08f6136cfc1181ee0b345748402f787dec99396aa5f1e3bc9ee11/simsimd-6.5.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4f1f20ee42d2aa57bb6cfb03c3d17c5c68cde987a71e3d421240aff159c004e8", size = 368280, upload-time = "2025-09-06T16:15:23.571Z" }, + { url = "https://files.pythonhosted.org/packages/43/72/471da3e5740f22ef7bcee74b3a1ec15ad4f4155c276a64cfd8e15d867118/simsimd-6.5.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:473fe6797cfdfc2f900abe51d8faa575743e6a051a5d3c8bf07eb64d8da20051", size = 1068715, upload-time = "2025-09-06T16:15:25.276Z" }, + { url = "https://files.pythonhosted.org/packages/d0/28/b0ba91295a3c1afd4e4158fcd654aa92946fb1e984b2be7f01fd00d50cc0/simsimd-6.5.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:de7ebf4918e94e1122e261778fac9a7397cceffc8fd8e3381301306a297f9678", size = 598538, upload-time = "2025-09-06T16:15:27.466Z" }, + { url = "https://files.pythonhosted.org/packages/02/99/1d3c3ecae923febe58e767f5d3da7a367d0132eda82b2955577d6c814ee1/simsimd-6.5.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5da3b88033315d654ac71feb68296fc0597d968ead995d8a53c24e31552a5344", size = 402560, upload-time = "2025-09-06T16:15:29.397Z" }, + { url = "https://files.pythonhosted.org/packages/38/08/4737bd305b4ba42f34e956a84a7b022dfd5d014bbbd9beebc5c404be8560/simsimd-6.5.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a4f4d711eb19278852f64f74b55fbf7a265b9993761f7d80e5ebadbd548bdbaa", size = 461351, upload-time = "2025-09-06T16:15:31.256Z" }, + { url = "https://files.pythonhosted.org/packages/4e/d8/50e0661ef867800cac673016b218ee82535ffd6a2f568550cab4f1c722e8/simsimd-6.5.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:22cfae73fb5c5220c4f3f1bfddde681cce7259b7e90e73a77225025a62511094", size = 372809, upload-time = "2025-09-06T16:15:32.983Z" }, + { url = "https://files.pythonhosted.org/packages/d0/30/bdcfed83a4aed6e249a5767265bbb09b6fdabc85d9ea0013c6e87fa5ff4c/simsimd-6.5.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:68754e56b9ca813b0fc73ea7ca04c303a36f3100811347009182646efaea4872", size = 1001540, upload-time = "2025-09-06T16:15:35.045Z" }, + { url = "https://files.pythonhosted.org/packages/13/6e/ccbd2c413bdf56ed08692bff59651aaa56f5bd695287dd05262db4535051/simsimd-6.5.3-cp311-cp311-win_amd64.whl", hash = "sha256:5e58bda40d247bf01b2cd50b841ab3376ec12ce022b8ed626b717f45b08eacd8", size = 94570, upload-time = "2025-09-06T16:15:36.577Z" }, + { url = "https://files.pythonhosted.org/packages/52/75/b880453dd12d9ba7002b02bf7e1a7b3800947a0872fb3b83c813a26a13d4/simsimd-6.5.3-cp311-cp311-win_arm64.whl", hash = "sha256:0608c74239d5f9fa9eda9b07479a710d807776c18bb7e0a3a8204dafb513425f", size = 59376, upload-time = "2025-09-06T16:15:37.876Z" }, + { url = "https://files.pythonhosted.org/packages/96/d8/ecb94ab75a0fbab1f9a36eac4cd7734836d7234788c2d3267d1f612e1cbd/simsimd-6.5.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:52495c13e8547c259a6da1ab5cbc95cb0ac4d2ca4ae33434b9514b64f39a122c", size = 177692, upload-time = "2025-09-06T16:15:39.199Z" }, + { url = "https://files.pythonhosted.org/packages/90/79/5bab3fd20625b5cb83435f2a0c307af7077394cb963ce9ae92d4b486f8a3/simsimd-6.5.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:11358046752d72059e425946ac00001704a47869cc0d05b9f750a64720a2a6a9", size = 134107, upload-time = "2025-09-06T16:15:40.739Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ac/99db6d29819250ca86bd403a5869901e10b8abfa85843a5c33b28dbfe194/simsimd-6.5.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be0f4921c370f715995789eb780315b0456d0b9937209caab0343b98bda5b668", size = 563233, upload-time = "2025-09-06T16:15:42.589Z" }, + { url = "https://files.pythonhosted.org/packages/5c/79/b3c00bdd2422de46a20add6e77dc34f66de5e157c28487a5e654fbf25965/simsimd-6.5.3-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:26c9920fe1bd3a1d15a24167e2d8777bed32b21b48868d0c785c1a821575bc56", size = 355529, upload-time = "2025-09-06T16:15:44.191Z" }, + { url = "https://files.pythonhosted.org/packages/6a/85/c65cbeb2fd33ffca41e76c79e73585da20e5d5ce4b0216681e61b643e657/simsimd-6.5.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bd0267b61c3128282b52388ce1390d95c8beab219da1b95d7aaadab9a18bf42b", size = 411360, upload-time = "2025-09-06T16:15:46.246Z" }, + { url = "https://files.pythonhosted.org/packages/89/25/ba0dbdc1614bb35ac5756eb50fc86e322c1701b723e86691dbec45fec765/simsimd-6.5.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:cab8670c7ed2754a6a5f3d2d568a43141c6494092fcc1693efecd20cefb51f61", size = 367963, upload-time = "2025-09-06T16:15:47.849Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f2/34bd80d5f9a1297f2cccab56d0b46fa017f6824ad162e2ea0646529dc539/simsimd-6.5.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:051c6493f07c4ec5938648accd351b16221a5d07633649b6f392e387811900a1", size = 1068417, upload-time = "2025-09-06T16:15:49.51Z" }, + { url = "https://files.pythonhosted.org/packages/e6/22/dea38422695e9637ae82d05e28e59b319664ae3f118a9bb1d1a9a7df53fa/simsimd-6.5.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8b1c26dd73960c9789e8e0f90750a2ede4e64120ad96b5f9ec46ef9e1f2039ac", size = 598297, upload-time = "2025-09-06T16:15:51.251Z" }, + { url = "https://files.pythonhosted.org/packages/3f/df/dc02eeac0eda0eb199039a4569bfcce3a98a78aab6af76dd1915b08433b3/simsimd-6.5.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c827f13caf47cc255dea3455e4f68da9930c396e77ac6f116ab82ecab5d9b1e4", size = 402229, upload-time = "2025-09-06T16:15:53.097Z" }, + { url = "https://files.pythonhosted.org/packages/a7/2a/e2c6c410bd29320c2666c03ffbba3314a07b2ffb338beabf9f98186c41d6/simsimd-6.5.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1cdcc253fdb9179b9273e4771c333b5d9adf99f911de0d8197a6ee5962bd9f86", size = 460979, upload-time = "2025-09-06T16:15:55.011Z" }, + { url = "https://files.pythonhosted.org/packages/d7/18/c91afb131ee2bd58ef4f05646c7c0c8d0b3a39a2f45386cd84c019030e3c/simsimd-6.5.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9d0bc9132bf2bb887246c784bf6a6c0b37a96af0d4aec7cc728e9b1274868bdb", size = 372616, upload-time = "2025-09-06T16:15:56.608Z" }, + { url = "https://files.pythonhosted.org/packages/c4/67/151b8855a0060cba592ef045f3655c144b19f98d896e1ad204c8e1dc6aeb/simsimd-6.5.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:94a989ec638e4ebe33c6aacd31fec8586480017909e7c5016c91005d52512cad", size = 1001276, upload-time = "2025-09-06T16:15:58.158Z" }, + { url = "https://files.pythonhosted.org/packages/5c/db/e458ae93987726f5b255148b259274c39e6f15b9d1158a0f0fa467539aa3/simsimd-6.5.3-cp312-cp312-win_amd64.whl", hash = "sha256:98af777ea1b227d42efdcb42fa5a667aa30c324665ec35425fcaa31152e4ccad", size = 94877, upload-time = "2025-09-06T16:15:59.848Z" }, + { url = "https://files.pythonhosted.org/packages/05/fa/a5c8533daf52021beece3666fe09d2d2f41bc807f4863ad582e7ee141649/simsimd-6.5.3-cp312-cp312-win_arm64.whl", hash = "sha256:6e6a0bd069e02bb1f2f88f53a0abfbcf8040d2764668569e519a3360b9303858", size = 59508, upload-time = "2025-09-06T16:16:01.195Z" }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + +[[package]] +name = "sklearn-crfsuite" +version = "0.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "python-crfsuite" }, + { name = "scikit-learn" }, + { name = "tabulate" }, + { name = "tqdm" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d7/d4/26211eb22127f8476589e92448273c698f4ac39fce2e300cd0a1f748de98/sklearn_crfsuite-0.5.0.tar.gz", hash = "sha256:136cb941ff5f7ce62a2c378988d1123ac1037241e73999ae404509a2135078e9", size = 26187, upload-time = "2024-06-18T11:08:30.42Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b2/11/a8370dd6fce65f8f4e74a0adffae72be9db5799d8ed8ddbf84415356a764/sklearn_crfsuite-0.5.0-py2.py3-none-any.whl", hash = "sha256:9cb7a881969b6426d28ada69ada7eff4c789a3c508eafc32d39c0d078ce3bebc", size = 10839, upload-time = "2024-06-18T11:08:22.743Z" }, +] + +[[package]] +name = "slicer" +version = "0.0.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d3/f9/b4bce2825b39b57760b361e6131a3dacee3d8951c58cb97ad120abb90317/slicer-0.0.8.tar.gz", hash = "sha256:2e7553af73f0c0c2d355f4afcc3ecf97c6f2156fcf4593955c3f56cf6c4d6eb7", size = 14894, upload-time = "2024-03-09T23:35:26.826Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/63/81/9ef641ff4e12cbcca30e54e72fb0951a2ba195d0cda0ba4100e532d929db/slicer-0.0.8-py3-none-any.whl", hash = "sha256:6c206258543aecd010d497dc2eca9d2805860a0b3758673903456b7df7934dc3", size = 15251, upload-time = "2024-03-09T07:03:07.708Z" }, +] + +[[package]] +name = "smart-open" +version = "7.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wrapt" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/67/9a/0a7acb748b86e2922982366d780ca4b16c33f7246fa5860d26005c97e4f3/smart_open-7.5.0.tar.gz", hash = "sha256:f394b143851d8091011832ac8113ea4aba6b92e6c35f6e677ddaaccb169d7cb9", size = 53920, upload-time = "2025-11-08T21:38:40.698Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ad/95/bc978be7ea0babf2fb48a414b6afaad414c6a9e8b1eafc5b8a53c030381a/smart_open-7.5.0-py3-none-any.whl", hash = "sha256:87e695c5148bbb988f15cec00971602765874163be85acb1c9fb8abc012e6599", size = 63940, upload-time = "2025-11-08T21:38:39.024Z" }, +] + +[[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 = "soupsieve" +version = "2.8" +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, upload-time = "2025-08-27T15:39:51.78Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/a0/bb38d3b76b8cae341dad93a2dd83ab7462e6dbcdd84d43f54ee60a8dc167/soupsieve-2.8-py3-none-any.whl", hash = "sha256:0cc76456a30e20f5d7f2e14a98a4ae2ee4e5abdc7c5ea0aafe795f344bc7984c", size = 36679, upload-time = "2025-08-27T15:39:50.179Z" }, +] + +[[package]] +name = "spacy" +version = "3.8.11" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "catalogue" }, + { name = "cymem" }, + { name = "jinja2" }, + { name = "murmurhash" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "preshed" }, + { name = "pydantic" }, + { name = "requests" }, + { name = "setuptools" }, + { name = "spacy-legacy" }, + { name = "spacy-loggers" }, + { name = "srsly" }, + { name = "thinc" }, + { name = "tqdm" }, + { name = "typer-slim" }, + { name = "wasabi" }, + { name = "weasel" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/59/9f/424244b0e2656afc9ff82fb7a96931a47397bfce5ba382213827b198312a/spacy-3.8.11.tar.gz", hash = "sha256:54e1e87b74a2f9ea807ffd606166bf29ac45e2bd81ff7f608eadc7b05787d90d", size = 1326804, upload-time = "2025-11-17T20:40:03.079Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/63/f23db7119e0bb7740d74eff4583543824be84e7c0aad1c87683b8f40a17e/spacy-3.8.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f9cc7f775cfc41ccb8be63bd6258a1ec4613d4ad3859f2ba2c079f34240b21f6", size = 6499016, upload-time = "2025-11-17T20:38:22.359Z" }, + { url = "https://files.pythonhosted.org/packages/5d/e4/e8c0f0561e8b29b4f38ba3d491fca427faa750765df3e27850036af28762/spacy-3.8.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:be9d665be8581926fba4303543ba189d34e8517803052551b000cf1a1af33b87", size = 6159121, upload-time = "2025-11-17T20:38:24.85Z" }, + { url = "https://files.pythonhosted.org/packages/15/7a/7ce7320f2a384023240fad0e6b7ffb2e3717ae4cc09ec0770706fd20c419/spacy-3.8.11-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:06e46ad776a1b20cc6296fe04890dea8a7b4e4653d7e8c143dd4a707f7ae2670", size = 30763429, upload-time = "2025-11-17T20:38:27.001Z" }, + { url = "https://files.pythonhosted.org/packages/db/36/b16df8f5ba8d5fc3d2b23f004eb55f3edf4f3345e743efdd560b6b20faf8/spacy-3.8.11-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e1b91199926eb9de507f7bfc63090b17ee9a12663bcfc76357560c2c7ef4750a", size = 31002535, upload-time = "2025-11-17T20:38:30.115Z" }, + { url = "https://files.pythonhosted.org/packages/6e/be/58183313f1401fff896d3dd8f8da977847fb1c205a2c2a8a7030e81da265/spacy-3.8.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a1d4c506adcbefd19ead59daca2e0e61ce669ff35372cc9c23aae1b292c57f94", size = 31033341, upload-time = "2025-11-17T20:38:33.06Z" }, + { url = "https://files.pythonhosted.org/packages/94/08/d490ed3a4ea070734c58cf1f2e3e6081a20630067bca2c58d5dbcfb36558/spacy-3.8.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d885a2bf427c854c5a5f1dda7451924a1f2c036aefaa2946c741201ff05a915a", size = 31882346, upload-time = "2025-11-17T20:38:35.596Z" }, + { url = "https://files.pythonhosted.org/packages/79/38/e64856b3f768754def0f5dc4c5fb3f692d96a193eec7e2eee03d37c233b6/spacy-3.8.11-cp310-cp310-win_amd64.whl", hash = "sha256:909d12ff2365c2e7ebf0258ddc566d2b361ef1fd2e7684ce1af5f7022111e366", size = 15346864, upload-time = "2025-11-17T20:38:37.95Z" }, + { url = "https://files.pythonhosted.org/packages/74/d3/0c795e6f31ee3535b6e70d08e89fc22247b95b61f94fc8334a01d39bf871/spacy-3.8.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a12d83e8bfba07563300ae5e0086548e41aa4bfe3734c97dda87e0eec813df0d", size = 6487958, upload-time = "2025-11-17T20:38:40.378Z" }, + { url = "https://files.pythonhosted.org/packages/4e/2a/83ca9b4d0a2b31adcf0ced49fa667212d12958f75d4e238618a60eb50b10/spacy-3.8.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e07a50b69500ef376326545353a470f00d1ed7203c76341b97242af976e3681a", size = 6148078, upload-time = "2025-11-17T20:38:42.524Z" }, + { url = "https://files.pythonhosted.org/packages/2c/f0/ff520df18a6152ba2dbf808c964014308e71a48feb4c7563f2a6cd6e668d/spacy-3.8.11-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:718b7bb5e83c76cb841ed6e407f7b40255d0b46af7101a426c20e04af3afd64e", size = 32056451, upload-time = "2025-11-17T20:38:44.92Z" }, + { url = "https://files.pythonhosted.org/packages/9d/3a/6c44c0b9b6a70595888b8d021514ded065548a5b10718ac253bd39f9fd73/spacy-3.8.11-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f860f9d51c1aeb2d61852442b232576e4ca4d239cb3d1b40ac452118b8eb2c68", size = 32302908, upload-time = "2025-11-17T20:38:47.672Z" }, + { url = "https://files.pythonhosted.org/packages/db/77/00e99e00efd4c2456772befc48400c2e19255140660d663e16b6924a0f2e/spacy-3.8.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ff8d928ce70d751b7bb27f60ee5e3a308216efd4ab4517291e6ff05d9b194840", size = 32280936, upload-time = "2025-11-17T20:38:50.893Z" }, + { url = "https://files.pythonhosted.org/packages/d8/da/692b51e9e5be2766d2d1fb9a7c8122cfd99c337570e621f09c40ce94ad17/spacy-3.8.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3f3cb91d7d42fafd92b8d5bf9f696571170d2f0747f85724a2c5b997753e33c9", size = 33117270, upload-time = "2025-11-17T20:38:53.596Z" }, + { url = "https://files.pythonhosted.org/packages/9b/13/a542ac9b61d071f3328fda1fd8087b523fb7a4f2c340010bc70b1f762485/spacy-3.8.11-cp311-cp311-win_amd64.whl", hash = "sha256:745c190923584935272188c604e0cc170f4179aace1025814a25d92ee90cf3de", size = 15348350, upload-time = "2025-11-17T20:38:56.833Z" }, + { url = "https://files.pythonhosted.org/packages/23/53/975c16514322f6385d6caa5929771613d69f5458fb24f03e189ba533f279/spacy-3.8.11-cp311-cp311-win_arm64.whl", hash = "sha256:27535d81d9dee0483b66660cadd93d14c1668f55e4faf4386aca4a11a41a8b97", size = 14701913, upload-time = "2025-11-17T20:38:59.507Z" }, + { url = "https://files.pythonhosted.org/packages/51/fb/01eadf4ba70606b3054702dc41fc2ccf7d70fb14514b3cd57f0ff78ebea8/spacy-3.8.11-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:aa1ee8362074c30098feaaf2dd888c829a1a79c4311eec1b117a0a61f16fa6dd", size = 6073726, upload-time = "2025-11-17T20:39:01.679Z" }, + { url = "https://files.pythonhosted.org/packages/3a/f8/07b03a2997fc2621aaeafae00af50f55522304a7da6926b07027bb6d0709/spacy-3.8.11-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:75a036d04c2cf11d6cb566c0a689860cc5a7a75b439e8fea1b3a6b673dabf25d", size = 5724702, upload-time = "2025-11-17T20:39:03.486Z" }, + { url = "https://files.pythonhosted.org/packages/13/0c/c4fa0f379dbe3258c305d2e2df3760604a9fcd71b34f8f65c23e43f4cf55/spacy-3.8.11-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cb599d2747d4a59a5f90e8a453c149b13db382a8297925cf126333141dbc4f7", size = 32727774, upload-time = "2025-11-17T20:39:05.894Z" }, + { url = "https://files.pythonhosted.org/packages/ce/8e/6a4ba82bed480211ebdf5341b0f89e7271b454307525ac91b5e447825914/spacy-3.8.11-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:94632e302ad2fb79dc285bf1e9e4d4a178904d5c67049e0e02b7fb4a77af85c4", size = 33215053, upload-time = "2025-11-17T20:39:08.588Z" }, + { url = "https://files.pythonhosted.org/packages/a6/bc/44d863d248e9d7358c76a0aa8b3f196b8698df520650ed8de162e18fbffb/spacy-3.8.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:aeca6cf34009d48cda9fb1bbfb532469e3d643817241a73e367b34ab99a5806f", size = 32074195, upload-time = "2025-11-17T20:39:11.601Z" }, + { url = "https://files.pythonhosted.org/packages/6f/7d/0b115f3f16e1dd2d3f99b0f89497867fc11c41aed94f4b7a4367b4b54136/spacy-3.8.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:368a79b8df925b15d89dccb5e502039446fb2ce93cf3020e092d5b962c3349b9", size = 32996143, upload-time = "2025-11-17T20:39:14.705Z" }, + { url = "https://files.pythonhosted.org/packages/7d/48/7e9581b476df76aaf9ee182888d15322e77c38b0bbbd5e80160ba0bddd4c/spacy-3.8.11-cp312-cp312-win_amd64.whl", hash = "sha256:88d65941a87f58d75afca1785bd64d01183a92f7269dcbcf28bd9d6f6a77d1a7", size = 14217511, upload-time = "2025-11-17T20:39:17.316Z" }, + { url = "https://files.pythonhosted.org/packages/7b/1f/307a16f32f90aa5ee7ad8d29ff8620a57132b80a4c8c536963d46d192e1a/spacy-3.8.11-cp312-cp312-win_arm64.whl", hash = "sha256:97b865d6d3658e2ab103a67d6c8a2d678e193e84a07f40d9938565b669ceee39", size = 13614446, upload-time = "2025-11-17T20:39:19.748Z" }, +] + +[[package]] +name = "spacy-legacy" +version = "3.0.12" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d9/79/91f9d7cc8db5642acad830dcc4b49ba65a7790152832c4eceb305e46d681/spacy-legacy-3.0.12.tar.gz", hash = "sha256:b37d6e0c9b6e1d7ca1cf5bc7152ab64a4c4671f59c85adaf7a3fcb870357a774", size = 23806, upload-time = "2023-01-23T09:04:15.104Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c3/55/12e842c70ff8828e34e543a2c7176dac4da006ca6901c9e8b43efab8bc6b/spacy_legacy-3.0.12-py2.py3-none-any.whl", hash = "sha256:476e3bd0d05f8c339ed60f40986c07387c0a71479245d6d0f4298dbd52cda55f", size = 29971, upload-time = "2023-01-23T09:04:13.45Z" }, +] + +[[package]] +name = "spacy-loggers" +version = "1.0.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/67/3d/926db774c9c98acf66cb4ed7faf6c377746f3e00b84b700d0868b95d0712/spacy-loggers-1.0.5.tar.gz", hash = "sha256:d60b0bdbf915a60e516cc2e653baeff946f0cfc461b452d11a4d5458c6fe5f24", size = 20811, upload-time = "2023-09-11T12:26:52.323Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/78/d1a1a026ef3af911159398c939b1509d5c36fe524c7b644f34a5146c4e16/spacy_loggers-1.0.5-py3-none-any.whl", hash = "sha256:196284c9c446cc0cdb944005384270d775fdeaf4f494d8e269466cfa497ef645", size = 22343, upload-time = "2023-09-11T12:26:50.586Z" }, +] + +[[package]] +name = "sqlalchemy" +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/f0/f2/840d7b9496825333f532d2e3976b8eadbf52034178aac53630d09fe6e1ef/sqlalchemy-2.0.44.tar.gz", hash = "sha256:0ae7454e1ab1d780aee69fd2aae7d6b8670a581d8847f2d1e0f7ddfbf47e5a22", size = 9819830, upload-time = "2025-10-10T14:39:12.935Z" } +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, upload-time = "2025-10-10T15:36:10.965Z" }, + { 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, upload-time = "2025-10-10T15:36:12.428Z" }, + { 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, upload-time = "2025-10-10T15:34:15.09Z" }, + { 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, upload-time = "2025-10-10T15:47:00.473Z" }, + { 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, upload-time = "2025-10-10T15:34:17.932Z" }, + { 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, upload-time = "2025-10-10T15:47:02.346Z" }, + { url = "https://files.pythonhosted.org/packages/43/f0/118355d4ad3c39d9a2f5ee4c7304a9665b3571482777357fa9920cd7a6b4/sqlalchemy-2.0.44-cp310-cp310-win32.whl", hash = "sha256:2fcc4901a86ed81dc76703f3b93ff881e08761c63263c46991081fd7f034b165", size = 2105624, upload-time = "2025-10-10T15:38:15.552Z" }, + { url = "https://files.pythonhosted.org/packages/61/83/6ae5f9466f8aa5d0dcebfff8c9c33b98b27ce23292df3b990454b3d434fd/sqlalchemy-2.0.44-cp310-cp310-win_amd64.whl", hash = "sha256:9919e77403a483ab81e3423151e8ffc9dd992c20d2603bf17e4a8161111e55f5", size = 2129240, upload-time = "2025-10-10T15:38:17.175Z" }, + { url = "https://files.pythonhosted.org/packages/e3/81/15d7c161c9ddf0900b076b55345872ed04ff1ed6a0666e5e94ab44b0163c/sqlalchemy-2.0.44-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0fe3917059c7ab2ee3f35e77757062b1bea10a0b6ca633c58391e3f3c6c488dd", size = 2140517, upload-time = "2025-10-10T15:36:15.64Z" }, + { url = "https://files.pythonhosted.org/packages/d4/d5/4abd13b245c7d91bdf131d4916fd9e96a584dac74215f8b5bc945206a974/sqlalchemy-2.0.44-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:de4387a354ff230bc979b46b2207af841dc8bf29847b6c7dbe60af186d97aefa", size = 2130738, upload-time = "2025-10-10T15:36:16.91Z" }, + { url = "https://files.pythonhosted.org/packages/cb/3c/8418969879c26522019c1025171cefbb2a8586b6789ea13254ac602986c0/sqlalchemy-2.0.44-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3678a0fb72c8a6a29422b2732fe423db3ce119c34421b5f9955873eb9b62c1e", size = 3304145, upload-time = "2025-10-10T15:34:19.569Z" }, + { url = "https://files.pythonhosted.org/packages/94/2d/fdb9246d9d32518bda5d90f4b65030b9bf403a935cfe4c36a474846517cb/sqlalchemy-2.0.44-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cf6872a23601672d61a68f390e44703442639a12ee9dd5a88bbce52a695e46e", size = 3304511, upload-time = "2025-10-10T15:47:05.088Z" }, + { url = "https://files.pythonhosted.org/packages/7d/fb/40f2ad1da97d5c83f6c1269664678293d3fe28e90ad17a1093b735420549/sqlalchemy-2.0.44-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:329aa42d1be9929603f406186630135be1e7a42569540577ba2c69952b7cf399", size = 3235161, upload-time = "2025-10-10T15:34:21.193Z" }, + { url = "https://files.pythonhosted.org/packages/95/cb/7cf4078b46752dca917d18cf31910d4eff6076e5b513c2d66100c4293d83/sqlalchemy-2.0.44-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:70e03833faca7166e6a9927fbee7c27e6ecde436774cd0b24bbcc96353bce06b", size = 3261426, upload-time = "2025-10-10T15:47:07.196Z" }, + { url = "https://files.pythonhosted.org/packages/f8/3b/55c09b285cb2d55bdfa711e778bdffdd0dc3ffa052b0af41f1c5d6e582fa/sqlalchemy-2.0.44-cp311-cp311-win32.whl", hash = "sha256:253e2f29843fb303eca6b2fc645aca91fa7aa0aa70b38b6950da92d44ff267f3", size = 2105392, upload-time = "2025-10-10T15:38:20.051Z" }, + { url = "https://files.pythonhosted.org/packages/c7/23/907193c2f4d680aedbfbdf7bf24c13925e3c7c292e813326c1b84a0b878e/sqlalchemy-2.0.44-cp311-cp311-win_amd64.whl", hash = "sha256:7a8694107eb4308a13b425ca8c0e67112f8134c846b6e1f722698708741215d5", size = 2130293, upload-time = "2025-10-10T15:38:21.601Z" }, + { url = "https://files.pythonhosted.org/packages/62/c4/59c7c9b068e6813c898b771204aad36683c96318ed12d4233e1b18762164/sqlalchemy-2.0.44-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:72fea91746b5890f9e5e0997f16cbf3d53550580d76355ba2d998311b17b2250", size = 2139675, upload-time = "2025-10-10T16:03:31.064Z" }, + { url = "https://files.pythonhosted.org/packages/d6/ae/eeb0920537a6f9c5a3708e4a5fc55af25900216bdb4847ec29cfddf3bf3a/sqlalchemy-2.0.44-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:585c0c852a891450edbb1eaca8648408a3cc125f18cf433941fa6babcc359e29", size = 2127726, upload-time = "2025-10-10T16:03:35.934Z" }, + { url = "https://files.pythonhosted.org/packages/d8/d5/2ebbabe0379418eda8041c06b0b551f213576bfe4c2f09d77c06c07c8cc5/sqlalchemy-2.0.44-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b94843a102efa9ac68a7a30cd46df3ff1ed9c658100d30a725d10d9c60a2f44", size = 3327603, upload-time = "2025-10-10T15:35:28.322Z" }, + { url = "https://files.pythonhosted.org/packages/45/e5/5aa65852dadc24b7d8ae75b7efb8d19303ed6ac93482e60c44a585930ea5/sqlalchemy-2.0.44-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:119dc41e7a7defcefc57189cfa0e61b1bf9c228211aba432b53fb71ef367fda1", size = 3337842, upload-time = "2025-10-10T15:43:45.431Z" }, + { url = "https://files.pythonhosted.org/packages/41/92/648f1afd3f20b71e880ca797a960f638d39d243e233a7082c93093c22378/sqlalchemy-2.0.44-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0765e318ee9179b3718c4fd7ba35c434f4dd20332fbc6857a5e8df17719c24d7", size = 3264558, upload-time = "2025-10-10T15:35:29.93Z" }, + { url = "https://files.pythonhosted.org/packages/40/cf/e27d7ee61a10f74b17740918e23cbc5bc62011b48282170dc4c66da8ec0f/sqlalchemy-2.0.44-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2e7b5b079055e02d06a4308d0481658e4f06bc7ef211567edc8f7d5dce52018d", size = 3301570, upload-time = "2025-10-10T15:43:48.407Z" }, + { url = "https://files.pythonhosted.org/packages/3b/3d/3116a9a7b63e780fb402799b6da227435be878b6846b192f076d2f838654/sqlalchemy-2.0.44-cp312-cp312-win32.whl", hash = "sha256:846541e58b9a81cce7dee8329f352c318de25aa2f2bbe1e31587eb1f057448b4", size = 2103447, upload-time = "2025-10-10T15:03:21.678Z" }, + { url = "https://files.pythonhosted.org/packages/25/83/24690e9dfc241e6ab062df82cc0df7f4231c79ba98b273fa496fb3dd78ed/sqlalchemy-2.0.44-cp312-cp312-win_amd64.whl", hash = "sha256:7cbcb47fd66ab294703e1644f78971f6f2f1126424d2b300678f419aa73c7b6e", size = 2130912, upload-time = "2025-10-10T15:03:24.656Z" }, + { url = "https://files.pythonhosted.org/packages/9c/5e/6a29fa884d9fb7ddadf6b69490a9d45fded3b38541713010dad16b77d015/sqlalchemy-2.0.44-py3-none-any.whl", hash = "sha256:19de7ca1246fbef9f9d1bff8f1ab25641569df226364a0e40457dc5457c54b05", size = 1928718, upload-time = "2025-10-10T15:29:45.32Z" }, +] + +[[package]] +name = "srsly" +version = "2.5.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "catalogue" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cf/77/5633c4ba65e3421b72b5b4bd93aa328360b351b3a1e5bf3c90eb224668e5/srsly-2.5.2.tar.gz", hash = "sha256:4092bc843c71b7595c6c90a0302a197858c5b9fe43067f62ae6a45bc3baa1c19", size = 492055, upload-time = "2025-11-17T14:11:02.543Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/58/ff9fd981b6e0fae261c48a3a941aeca5735eace4a137de883c8d69029bc7/srsly-2.5.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5491fe0683da900cd0c563538510c70a007380e1f6b29ebbb5225e7590981e2a", size = 655635, upload-time = "2025-11-17T14:09:41.167Z" }, + { url = "https://files.pythonhosted.org/packages/fd/a6/5b03c2a3b407caec3e7a5df61523154de3c5d36dc2f9328be91d3df368d5/srsly-2.5.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7375c2955935b73a6cad3851fe819c2f4ec506504afe7ca92b917555e6850fae", size = 653395, upload-time = "2025-11-17T14:09:42.827Z" }, + { url = "https://files.pythonhosted.org/packages/62/5d/1829a208d6d291c1ab3b81acd6e7a9f11984afc674ba2778e57984eee1a7/srsly-2.5.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0709a97ca463c1e85b03432c7d8028c82439f0248816707bafc553ffe66ec6f9", size = 1121898, upload-time = "2025-11-17T14:09:44.461Z" }, + { url = "https://files.pythonhosted.org/packages/c6/ce/71766be1488ce4058dc5eded6f5c0ce7cbb18ff7263f3cc718fe8b1033ad/srsly-2.5.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ea2ee0122312802ed531fee6de679d74ce99ce8addce49aff8d52ee670d810f8", size = 1122831, upload-time = "2025-11-17T14:09:46.011Z" }, + { url = "https://files.pythonhosted.org/packages/ab/5c/259e5b0e70c22c5bbd1327a79bb4b2d75efb38295475229e9310251c240e/srsly-2.5.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c2e9fc418585832c7ce01bfc7fe85b96afe11165eb9a31ff0ed52aa3e32ec08b", size = 1080719, upload-time = "2025-11-17T14:09:47.685Z" }, + { url = "https://files.pythonhosted.org/packages/32/c4/20face1113cfa436434c7c152b374edae1631177d0d44dd60103297ffe03/srsly-2.5.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3df0ef22d571e733b181ac488823b01f4dd13da23497f46956839c718e48f36b", size = 1092783, upload-time = "2025-11-17T14:09:49.295Z" }, + { url = "https://files.pythonhosted.org/packages/c1/aa/16c405cf830bf3d843a631d62681403eb44563e27a42648f417f40209045/srsly-2.5.2-cp310-cp310-win_amd64.whl", hash = "sha256:a116b926dd24702f5474f6367d8083412f218ddf82d5c7b5831a7b2ba3d8bd55", size = 654041, upload-time = "2025-11-17T14:09:51.056Z" }, + { url = "https://files.pythonhosted.org/packages/59/6e/2e3d07b38c1c2e98487f0af92f93b392c6741062d85c65cdc18c7b77448a/srsly-2.5.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e7e07babdcece2405b32c9eea25ef415749f214c889545e38965622bb66837ce", size = 655286, upload-time = "2025-11-17T14:09:52.468Z" }, + { url = "https://files.pythonhosted.org/packages/a1/e7/587bcade6b72f919133e587edf60e06039d88049aef9015cd0bdea8df189/srsly-2.5.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1718fe40b73e5cc73b14625233f57e15fb23643d146f53193e8fe653a49e9a0f", size = 653094, upload-time = "2025-11-17T14:09:53.837Z" }, + { url = "https://files.pythonhosted.org/packages/8d/24/5c3aabe292cb4eb906c828f2866624e3a65603ef0a73e964e486ff146b84/srsly-2.5.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d7b07e6103db7dd3199c0321935b0c8b9297fd6e018a66de97dc836068440111", size = 1141286, upload-time = "2025-11-17T14:09:55.535Z" }, + { url = "https://files.pythonhosted.org/packages/2a/fe/2cbdcef2495e0c40dafb96da205d9ab3b9e59f64938277800bf65f923281/srsly-2.5.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f2dedf03b2ae143dd70039f097d128fb901deba2482c3a749ac0a985ac735aad", size = 1144667, upload-time = "2025-11-17T14:09:57.24Z" }, + { url = "https://files.pythonhosted.org/packages/91/7c/9a2c9d8141daf7b7a6f092c2be403421a0ab280e7c03cc62c223f37fdf47/srsly-2.5.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9d5be1d8b79a4c4180073461425cb49c8924a184ab49d976c9c81a7bf87731d9", size = 1103935, upload-time = "2025-11-17T14:09:58.576Z" }, + { url = "https://files.pythonhosted.org/packages/f1/ad/8ae727430368fedbb1a7fa41b62d7a86237558bc962c5c5a9aa8bfa82548/srsly-2.5.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c8e42d6bcddda2e6fc1a8438cc050c4a36d0e457a63bcc7117d23c5175dfedec", size = 1117985, upload-time = "2025-11-17T14:10:00.348Z" }, + { url = "https://files.pythonhosted.org/packages/60/69/d6afaef1a8d5192fd802752115c7c3cc104493a7d604b406112b8bc2b610/srsly-2.5.2-cp311-cp311-win_amd64.whl", hash = "sha256:e7362981e687eead00248525c3ef3b8ddd95904c93362c481988d91b26b6aeef", size = 654148, upload-time = "2025-11-17T14:10:01.772Z" }, + { url = "https://files.pythonhosted.org/packages/8f/1c/21f658d98d602a559491b7886c7ca30245c2cd8987ff1b7709437c0f74b1/srsly-2.5.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6f92b4f883e6be4ca77f15980b45d394d310f24903e25e1b2c46df783c7edcce", size = 656161, upload-time = "2025-11-17T14:10:03.181Z" }, + { url = "https://files.pythonhosted.org/packages/2f/a2/bc6fd484ed703857043ae9abd6c9aea9152f9480a6961186ee6c1e0c49e8/srsly-2.5.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ac4790a54b00203f1af5495b6b8ac214131139427f30fcf05cf971dde81930eb", size = 653237, upload-time = "2025-11-17T14:10:04.636Z" }, + { url = "https://files.pythonhosted.org/packages/ab/ea/e3895da29a15c8d325e050ad68a0d1238eece1d2648305796adf98dcba66/srsly-2.5.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ce5c6b016050857a7dd365c9dcdd00d96e7ac26317cfcb175db387e403de05bf", size = 1174418, upload-time = "2025-11-17T14:10:05.945Z" }, + { url = "https://files.pythonhosted.org/packages/a6/a5/21996231f53ee97191d0746c3a672ba33a4d86a19ffad85a1c0096c91c5f/srsly-2.5.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:539c6d0016e91277b5e9be31ebed03f03c32580d49c960e4a92c9003baecf69e", size = 1183089, upload-time = "2025-11-17T14:10:07.335Z" }, + { url = "https://files.pythonhosted.org/packages/7b/df/eb17aa8e4a828e8df7aa7dc471295529d9126e6b710f1833ebe0d8568a8e/srsly-2.5.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9f24b2c4f4c29da04083f09158543eb3f8893ba0ac39818693b3b259ee8044f0", size = 1122594, upload-time = "2025-11-17T14:10:08.899Z" }, + { url = "https://files.pythonhosted.org/packages/80/74/1654a80e6c8ec3ee32370ea08a78d3651e0ba1c4d6e6be31c9efdb9a2d10/srsly-2.5.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d34675047460a3f6999e43478f40d9b43917ea1e93a75c41d05bf7648f3e872d", size = 1139594, upload-time = "2025-11-17T14:10:10.286Z" }, + { url = "https://files.pythonhosted.org/packages/73/aa/8393344ca7f0e81965febba07afc5cad68335ed0426408d480b861ab915b/srsly-2.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:81fd133ba3c66c07f0e3a889d2b4c852984d71ea833a665238a9d47d8e051ba5", size = 654750, upload-time = "2025-11-17T14:10:11.637Z" }, +] + +[[package]] +name = "stringzilla" +version = "4.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0d/67/55c29ecd1eb2e349b867fa01a8e72807a71791363ff3538796d4908c3785/stringzilla-4.2.3.tar.gz", hash = "sha256:7557b395729228c4c8f13bca01a4958dc7adaf1f5ea2a812b78d265ec9c0ab14", size = 494108, upload-time = "2025-10-27T18:36:48.169Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b2/34/0fa8b6a26c7a4f084dea821809c6c4c6fcdf7863cd30020ad0389091d0b2/stringzilla-4.2.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:c0151d65a8fa855f627f3c0e6b37381cb836899d44373e2985d7650c09e6eb91", size = 138577, upload-time = "2025-10-27T18:34:22.763Z" }, + { url = "https://files.pythonhosted.org/packages/62/d6/3ef6d37b8e8ddd0df9d9a1c477b449b23b05d7771801d296744e051b37f1/stringzilla-4.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8495311a034fe93652385e3fa0fb3644bac1b7902f296be04bbbcd3c861af0f8", size = 133747, upload-time = "2025-10-27T18:34:26.237Z" }, + { url = "https://files.pythonhosted.org/packages/5f/b2/fea3371e2aa01f1139bf069d6fd8839c9272fc88f8ec6d0e7cf031985697/stringzilla-4.2.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d239bfdb5b9da5c7824427c44ff118d7e49101ccd7deff4728ff5e9b24d32f32", size = 424745, upload-time = "2025-10-27T18:34:27.302Z" }, + { url = "https://files.pythonhosted.org/packages/94/63/9a374c138e69a2ddd31deeb7db03fa6fc456280a8181a95fea0a45f4cd8f/stringzilla-4.2.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:20396fe70a35c31d09810710d3d9c8ec8330e85df499aba8e97a1cbd888a9644", size = 385182, upload-time = "2025-10-27T18:34:28.401Z" }, + { url = "https://files.pythonhosted.org/packages/00/11/9e099b939c0eb06ba3642d93fe482faa8c12ac9b2fd5d4de362d8d17d953/stringzilla-4.2.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9eaa55b894713527a71ec66fe5e6f54e7761170d4dd6fc70b19b8d7cf7299be7", size = 351198, upload-time = "2025-10-27T18:34:30.003Z" }, + { url = "https://files.pythonhosted.org/packages/13/92/53abd1dfe62f2e98e39aeb3149714ddcee88ef3b40e63b6910e89d6b26aa/stringzilla-4.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1ac76a5dc5fa476a58eb03a42b46fa5801c46ca7a31c7a72e04f97e6f85e1d9c", size = 592198, upload-time = "2025-10-27T18:34:31.37Z" }, + { url = "https://files.pythonhosted.org/packages/20/1d/e7abd4be77953095bd3c0ee8fd699505b69dab079ef70a0d00cdbfa2e25a/stringzilla-4.2.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ac464db30a4c35e6b4c2436b9bd2c9098ac90b949322db010b8b378753b87fc", size = 337732, upload-time = "2025-10-27T18:34:32.54Z" }, + { url = "https://files.pythonhosted.org/packages/1a/32/12a91f35cf65e7c3f5271a0200883bec6ac990e8df988e772fe5de8ff3eb/stringzilla-4.2.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8354aedd17456e2d3aec44a7e38e5800236f66fea934fcaae953032a82a6f170", size = 405967, upload-time = "2025-10-27T18:34:33.815Z" }, + { url = "https://files.pythonhosted.org/packages/48/35/816ad52e7582042b54812816e5ce4ce43acee067d214630a175dfe43436c/stringzilla-4.2.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:49377324523665b2ea6135bffefbfb0b87920a355ee0ad821e3d3f44b9738fcc", size = 342668, upload-time = "2025-10-27T18:34:35.257Z" }, + { url = "https://files.pythonhosted.org/packages/36/b5/0ed35964c561ccf4d3df28f3682806fdb4026fb9c25ebbd7eab2f45204fc/stringzilla-4.2.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c045c3f5f7654bae902c262dab4c730709dd570368cb45e64312fa3b5ceb6010", size = 324659, upload-time = "2025-10-27T18:34:36.966Z" }, + { url = "https://files.pythonhosted.org/packages/3a/31/697d59b8aeed31e2899e4f520e2f7a5000631863f8687f5a9e1ce90ef985/stringzilla-4.2.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e0110eb92ed2295c23f7f7664b427948ec0e1c26f43d213bba11d7d755b2088b", size = 355314, upload-time = "2025-10-27T18:34:38.333Z" }, + { url = "https://files.pythonhosted.org/packages/59/da/26a4c08cb51fdc79479dc3608b08a96aff3f73f14c6659ead1d4bace04a1/stringzilla-4.2.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:96b8905b4e260e50141925e6a5f8a390899181f51ebc925bdbbaefd731f1f77d", size = 348481, upload-time = "2025-10-27T18:34:39.548Z" }, + { url = "https://files.pythonhosted.org/packages/ed/95/a7d2e66586c23b26f88e3246a40f836794405acfc189a87fe77a1dfa8115/stringzilla-4.2.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0a647fa41828a069cdaa0a95dfdda9e022203575bd357e6f2b146ebd36e3317f", size = 576527, upload-time = "2025-10-27T18:34:40.763Z" }, + { url = "https://files.pythonhosted.org/packages/8a/63/5d25a45b7797fab42b44de3f3850c2d0752ace392ca4157511fc38cfb9df/stringzilla-4.2.3-cp310-cp310-win32.whl", hash = "sha256:a0d25423fe817bc487b1601338960b66d7fbb66688999fdede65c70b5c527c96", size = 90925, upload-time = "2025-10-27T18:34:42.338Z" }, + { url = "https://files.pythonhosted.org/packages/e1/e3/b89e9518c7bbf33dcd7a7ffff290c67b59f1c80b898e3faec8206c38eae4/stringzilla-4.2.3-cp310-cp310-win_amd64.whl", hash = "sha256:0e71cdc5454c62db690909f88b24d3f411183d4823d685ddb5a334a32dd54f64", size = 116231, upload-time = "2025-10-27T18:34:43.824Z" }, + { url = "https://files.pythonhosted.org/packages/aa/24/235162afa68c0adde17deefde83da2c31ecf09031e845e5750d9f9f3c1f5/stringzilla-4.2.3-cp310-cp310-win_arm64.whl", hash = "sha256:e28e64abe647c6124dd69113cd18a54c1ac480cfbcd69252f15daee4c79e313b", size = 99614, upload-time = "2025-10-27T18:34:45.231Z" }, + { url = "https://files.pythonhosted.org/packages/29/91/b89d56941b33311eb2db92cf515ed7aeac2c7265d2bc3752893f185f075d/stringzilla-4.2.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:131e2ff7d799098fea60c26813eb13229ecc7272622342e0b69c217b3d02c72f", size = 138576, upload-time = "2025-10-27T18:34:46.98Z" }, + { url = "https://files.pythonhosted.org/packages/f6/5f/a2a25cd372b2d2a780d98a4f145525f7258c97c8943d1df6f47b4f0e60b6/stringzilla-4.2.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d1eacebae38e2f98c5c29bc6acbbc3da908934d7e185edc216cd39f741277726", size = 133735, upload-time = "2025-10-27T18:34:48.114Z" }, + { url = "https://files.pythonhosted.org/packages/71/81/31329d3e53f0d8378ebdd687dfdb609820ac99204b4b366a306ec54eb67c/stringzilla-4.2.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9d03745a1b354d71408e6eb4949e2399919160592249efbf100475ecc6963a95", size = 428344, upload-time = "2025-10-27T18:34:49.534Z" }, + { url = "https://files.pythonhosted.org/packages/1b/cb/3f8df3d3d6a2d964f509dca70892c4911beee5aa98c97d85fe8c256621ca/stringzilla-4.2.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:476a4c0ede0c034f57d25b944900eea77ecaad2520f84c916eb2d2d66c6392ad", size = 390774, upload-time = "2025-10-27T18:34:51.631Z" }, + { url = "https://files.pythonhosted.org/packages/3e/2c/8c36be7515cb097a85e348559bbe57e24cbf0e19728fa254afb002989cf1/stringzilla-4.2.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bd8a0167151519d5d6a7da8401ea77d053b8d199170c3f524f8acc21450da72b", size = 356831, upload-time = "2025-10-27T18:34:52.91Z" }, + { url = "https://files.pythonhosted.org/packages/b7/eb/7692bc17d48259e4f7566a920d1e4a2005aa8075b8ad103414708e1d8c49/stringzilla-4.2.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:27344e01143926a6175a9e65037b8e15acee6ec7faac0f88044d09e08aee9064", size = 597224, upload-time = "2025-10-27T18:34:54.175Z" }, + { url = "https://files.pythonhosted.org/packages/f0/d2/bb22f742aecc7a43a7720e07f3dbb4eb5cfb89e15e8bfd3b821f39fb8a27/stringzilla-4.2.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4ceee50a8034f7c8c22bbebb54fdf1e63054bf2f7aeeb432538da2c63d54a871", size = 343089, upload-time = "2025-10-27T18:34:55.509Z" }, + { url = "https://files.pythonhosted.org/packages/98/f6/d1c2e5af75f2b070abc6da978431c21c7363c305bd61410bc50d8e39ddf0/stringzilla-4.2.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:67a68a9d52f0d8d82c46e741d2c081a479c2ff502c4fccc490c2275818f83a72", size = 411010, upload-time = "2025-10-27T18:34:56.853Z" }, + { url = "https://files.pythonhosted.org/packages/70/d6/70935e630f8fd0473dde0adc3f766e0990b284171fd6f81f4b34db1322a2/stringzilla-4.2.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:5ed329f73dd10db8eaf978bf25f107533bbc77a3de04192e59cd371546bf8a0d", size = 347422, upload-time = "2025-10-27T18:34:58.355Z" }, + { url = "https://files.pythonhosted.org/packages/6e/20/456f18a37bd0230bfa9fbc853aeedb7533d0e085854eceb2f2037b1fe683/stringzilla-4.2.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:b55a1de5da869792b667eb825c09ee31412ad68d4c115d39c5b26a33c91a4d11", size = 329700, upload-time = "2025-10-27T18:34:59.565Z" }, + { url = "https://files.pythonhosted.org/packages/03/83/857b673364f5c8f7531a4e9f5fe2543b0f41bc9b4b95d75d246f0ff1280f/stringzilla-4.2.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b4f8f1b82321a8e1ff8e6ef8ca5b6ee73575a91245ca7689ce573844ae7fc17d", size = 360152, upload-time = "2025-10-27T18:35:01.324Z" }, + { url = "https://files.pythonhosted.org/packages/8f/34/34c07160c590ee1beb436db56956c6af57fb00baaa5b9507b98e8d86472a/stringzilla-4.2.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:7090ade734c203597ceeafbb5f702468bcf75a7ee8d16271ec1a90c81b391c48", size = 353567, upload-time = "2025-10-27T18:35:02.52Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f3/f53152383bafb7b08a110767b08ea963fb7d754ba5a5fc25192b25c4f333/stringzilla-4.2.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:603bb59819eb9b59d3f372a1ed0f2dfa6157cddf4bc804879db2693e42b95253", size = 581658, upload-time = "2025-10-27T18:35:03.749Z" }, + { url = "https://files.pythonhosted.org/packages/da/f7/d5110af7ecb1724436fec2cea3e46b993d07b60b41797dc71353a645030f/stringzilla-4.2.3-cp311-cp311-win32.whl", hash = "sha256:1f4d06ea78ec5b8a1d392c67f014ced96106e2b9607b5d71a5dbfb61687e6e8e", size = 90933, upload-time = "2025-10-27T18:35:04.971Z" }, + { url = "https://files.pythonhosted.org/packages/83/c2/aac1488eb8ec6142c8668f1568c1992447fdbef4a5c5f355ab91c435c4f7/stringzilla-4.2.3-cp311-cp311-win_amd64.whl", hash = "sha256:6d754fce563dd94b3935c80c0cafd8fb3b5c180b035b03bd936ee8661d2a4bcd", size = 116131, upload-time = "2025-10-27T18:35:06.364Z" }, + { url = "https://files.pythonhosted.org/packages/c5/72/c1d0055847e7f63b5dda6e77a18b8f504737beece71008d02201811e75fc/stringzilla-4.2.3-cp311-cp311-win_arm64.whl", hash = "sha256:7e06c56e73c3db14068a53d8825d7e519022f4e5186c69cfbb07531d5b22b427", size = 99608, upload-time = "2025-10-27T18:35:08.959Z" }, + { url = "https://files.pythonhosted.org/packages/10/7f/f40115173ab86e82882598cdb0343ad841c197cc583a8e53fd8ed7da8426/stringzilla-4.2.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:33a330673230db0c9218322768adaeca29d2ec749411c67fc0a6af5ed372e32c", size = 139198, upload-time = "2025-10-27T18:35:10.147Z" }, + { url = "https://files.pythonhosted.org/packages/b6/33/c7cb3c67b735d18df80bf226563920f1aa1afc3ba18b2dcf45620797cfe8/stringzilla-4.2.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aaf95644cdddd322f1cf7a85b695ab474af93c0092461a57bd49c3e14ef332dd", size = 134090, upload-time = "2025-10-27T18:35:11.906Z" }, + { url = "https://files.pythonhosted.org/packages/7a/93/4d63b31bd9ef2cacb193a4c9f7eb1c5dfd03c7b97e6d2005b86a540efd90/stringzilla-4.2.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4422ed6794c48c4cb81e53773ea13b9f09a919f260cb8aa7aba557ff690f6ceb", size = 428164, upload-time = "2025-10-27T18:35:13.527Z" }, + { url = "https://files.pythonhosted.org/packages/39/c9/95ecd11bf712e13a5409cb1be4a212d806816b0c5a477ac0837e92ec1a46/stringzilla-4.2.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c585ed5b1b98044e5835fa809537aa147519762303073cadc0ac1af2678dd959", size = 390859, upload-time = "2025-10-27T18:35:14.798Z" }, + { url = "https://files.pythonhosted.org/packages/11/b8/db70267d90da993d4ce2b4ba4ab33082bbf298fb1e1ce4023b2aea291f9d/stringzilla-4.2.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7264d9e1be199e1cd35163062add10d3c2bee16fb66ab6ae8b444864546a32e1", size = 357126, upload-time = "2025-10-27T18:35:16.169Z" }, + { url = "https://files.pythonhosted.org/packages/5a/1b/1eaea0cae6cf28604817bb822b277363e489c62b455e40a41dc4de1b352a/stringzilla-4.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:313521205f6599344c0af804979e53b71095166b49478d6edd251d0b80506043", size = 599065, upload-time = "2025-10-27T18:35:17.487Z" }, + { url = "https://files.pythonhosted.org/packages/b2/b1/46346785ebea882b4a2818036ac22da47ccce205849ec84919145c3b69f1/stringzilla-4.2.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c53698dd56cf1a70cb1a74adfa19c88515e7def697af9bf3931a1c4d7d8dac5", size = 345039, upload-time = "2025-10-27T18:35:18.808Z" }, + { url = "https://files.pythonhosted.org/packages/26/bb/f45a0c552310f1a056d391af03c7f53397a8c3d2f3219276f975a24122e1/stringzilla-4.2.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:471a4d4e910184a4a7e44223652334b32cf323f898e7adb114e349031a6e7ad7", size = 410428, upload-time = "2025-10-27T18:35:20.51Z" }, + { url = "https://files.pythonhosted.org/packages/83/6a/89ae5ef59ee2825f5668998f9abc8ec6996deef4664c4f80c3f601d935d6/stringzilla-4.2.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:c8af9108f2158acebf694fdcaefc2a6dbacab410dd532f3538b4be8e3b1ea9ad", size = 350216, upload-time = "2025-10-27T18:35:21.857Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a3/5dbe9c3c978558ed209748090239feff58d2326d6a110e1303501eb52a90/stringzilla-4.2.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:fe7283d04790f648196ae1041bb54d35ffd609143cea04232b6cc824d01e84c8", size = 331161, upload-time = "2025-10-27T18:35:23.548Z" }, + { url = "https://files.pythonhosted.org/packages/4a/e3/1c54546212efff34f5218641a7f715406e1b2e10d87d2f9adf5dcc452d1a/stringzilla-4.2.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:2506e3d8a1f6ecdee8523f6f1c9335024ffa46ab4cc6d0443bfac713f05ae851", size = 360494, upload-time = "2025-10-27T18:35:24.858Z" }, + { url = "https://files.pythonhosted.org/packages/22/4a/2bdd15a50e56969c3203629d40126d820949f7a9dbcec7c539d808e5c5ba/stringzilla-4.2.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5289f510838c819eb4f197250189e9abf1925626eca84e3a1a2fb5840c71dc67", size = 354818, upload-time = "2025-10-27T18:35:26.452Z" }, + { url = "https://files.pythonhosted.org/packages/fb/c9/622321141599ff31996b3d59193f935e67c6a4867fa141c40396d4118d1a/stringzilla-4.2.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:55fa46ee5defae9c2adf22895098c044e654bb458b488a8035fcc40e4513f63d", size = 583524, upload-time = "2025-10-27T18:35:28.234Z" }, + { url = "https://files.pythonhosted.org/packages/86/76/9c895cb16060e444526d55ed0543a3a1e2c386d177e845b53cb7633a0d8d/stringzilla-4.2.3-cp312-cp312-win32.whl", hash = "sha256:acf256cd9892cae3870b87beb8b9bcae05f1f8fbf6d0fb37eec9ee4071934a00", size = 91130, upload-time = "2025-10-27T18:35:29.627Z" }, + { url = "https://files.pythonhosted.org/packages/af/0e/79f040dfa6a06a92413fa78d288f6922a0fe6c3a10058606e0d556079437/stringzilla-4.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:2c915a0215827a298cd64b40609be130a1ea6161b7cd2003ba65a148902db314", size = 116264, upload-time = "2025-10-27T18:35:31.012Z" }, + { url = "https://files.pythonhosted.org/packages/cb/84/49afd972207b7b67a68d258aab112590664c35e52b85fc8afce10e7dae00/stringzilla-4.2.3-cp312-cp312-win_arm64.whl", hash = "sha256:2e3d1896478307218a0c0fabdd9bc3013dc1680cf7c4b815da0b8da9b5fce35b", size = 99811, upload-time = "2025-10-27T18:35:32.501Z" }, +] + +[[package]] +name = "sympy" +version = "1.14.0" +source = { registry = "https://download.pytorch.org/whl/cu126" } +dependencies = [ + { name = "mpmath" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5" }, +] + +[[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" } +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" }, +] + +[[package]] +name = "theseus" +version = "2.0.0" +source = { editable = "." } +dependencies = [ + { name = "deepdiff" }, + { name = "gdown" }, + { name = "hydra-core" }, + { name = "kaleido" }, + { name = "lightning" }, + { name = "loguru" }, + { name = "matplotlib" }, + { name = "omegaconf" }, + { name = "plotly" }, + { name = "pyyaml" }, + { name = "rich" }, + { name = "scikit-learn" }, + { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scipy", version = "1.16.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "seaborn" }, + { name = "tabulate" }, + { name = "torch" }, + { name = "torchvision" }, + { name = "tqdm" }, + { name = "wandb" }, + { name = "webcolors" }, +] + +[package.optional-dependencies] +all = [ + { name = "albumentations" }, + { name = "captum" }, + { name = "catboost" }, + { name = "elasticsearch" }, + { name = "ensemble-boxes" }, + { name = "grad-cam" }, + { name = "lightgbm" }, + { name = "nltk" }, + { name = "numpy" }, + { name = "omegaconf" }, + { name = "optuna" }, + { name = "optuna-integration", extra = ["wandb"] }, + { name = "py-vncorenlp" }, + { name = "pycocotools" }, + { name = "pyvi" }, + { name = "rank-bm25" }, + { name = "scikit-plot" }, + { name = "segmentation-models-pytorch" }, + { name = "sentence-transformers" }, + { name = "shap" }, + { name = "spacy" }, + { name = "timm" }, + { name = "transformers" }, + { name = "xgboost" }, +] +cv = [ + { name = "albumentations" }, + { name = "omegaconf" }, +] +cv-classification = [ + { name = "grad-cam" }, + { name = "scikit-plot" }, + { name = "timm" }, +] +cv-detection = [ + { name = "ensemble-boxes" }, + { name = "pycocotools" }, +] +cv-semantic = [ + { name = "segmentation-models-pytorch" }, +] +dev = [ + { name = "pre-commit" }, + { name = "pytest" }, + { name = "pytest-order" }, + { name = "ruff" }, +] +ml = [ + { name = "catboost" }, + { name = "lightgbm" }, + { name = "numpy" }, + { name = "optuna" }, + { name = "optuna-integration", extra = ["wandb"] }, + { name = "shap" }, + { name = "xgboost" }, +] +nlp = [ + { name = "captum" }, + { name = "nltk" }, + { name = "py-vncorenlp" }, + { name = "pyvi" }, + { name = "spacy" }, + { name = "transformers" }, +] +nlp-retrieval = [ + { name = "elasticsearch" }, + { name = "rank-bm25" }, + { name = "sentence-transformers" }, +] + +[package.metadata] +requires-dist = [ + { name = "albumentations", marker = "extra == 'cv'", specifier = ">=1.4.0,<2.0.0" }, + { name = "captum", marker = "extra == 'nlp'", specifier = ">=0.7.0" }, + { name = "catboost", marker = "extra == 'ml'", specifier = "==1.2.8" }, + { name = "deepdiff", specifier = ">=7.0.0" }, + { name = "elasticsearch", marker = "extra == 'nlp-retrieval'", specifier = ">=8.12.0" }, + { name = "ensemble-boxes", marker = "extra == 'cv-detection'", specifier = ">=1.0.9" }, + { name = "gdown", specifier = ">=5.1.0" }, + { name = "grad-cam", marker = "extra == 'cv-classification'", specifier = ">=1.5.0" }, + { name = "hydra-core", specifier = ">=1.3.2" }, + { name = "kaleido", specifier = ">=0.2.1" }, + { name = "lightgbm", marker = "extra == 'ml'", specifier = "==4.6.0" }, + { name = "lightning", specifier = ">=2.4.0" }, + { name = "loguru", specifier = ">=0.7.0" }, + { name = "matplotlib", specifier = ">=3.8.0" }, + { name = "nltk", marker = "extra == 'nlp'", specifier = ">=3.8.0" }, + { name = "numpy", marker = "extra == 'ml'", specifier = "==1.26.4" }, + { name = "omegaconf", specifier = ">=2.3.0" }, + { name = "omegaconf", marker = "extra == 'cv'", specifier = ">=2.3.0" }, + { name = "optuna", marker = "extra == 'ml'", specifier = ">=3.6.0" }, + { name = "optuna-integration", extras = ["wandb"], marker = "extra == 'ml'", specifier = ">=4.8.0" }, + { name = "plotly", specifier = ">=5.18.0" }, + { name = "pre-commit", marker = "extra == 'dev'", specifier = ">=3.6.0" }, + { name = "py-vncorenlp", marker = "extra == 'nlp'", specifier = ">=0.1.3" }, + { name = "pycocotools", marker = "extra == 'cv-detection'", specifier = ">=2.0.7" }, + { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0.0" }, + { name = "pytest-order", marker = "extra == 'dev'", specifier = ">=1.2.0" }, + { name = "pyvi", marker = "extra == 'nlp'", specifier = ">=0.1.1" }, + { name = "pyyaml", specifier = ">=6.0.1" }, + { name = "rank-bm25", marker = "extra == 'nlp-retrieval'", specifier = ">=0.2.2" }, + { name = "rich", specifier = ">=14.3.3" }, + { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.5.0" }, + { name = "scikit-learn", specifier = ">=1.7.2" }, + { name = "scikit-plot", marker = "extra == 'cv-classification'" }, + { name = "scipy", specifier = ">=1.11.4" }, + { name = "seaborn", specifier = ">=0.13.0" }, + { name = "segmentation-models-pytorch", marker = "extra == 'cv-semantic'", specifier = ">=0.3.3" }, + { name = "sentence-transformers", marker = "extra == 'nlp-retrieval'", specifier = ">=3.0.0" }, + { name = "shap", marker = "extra == 'ml'", specifier = "==0.47.1" }, + { name = "spacy", marker = "extra == 'nlp'", specifier = ">=3.7.0" }, + { name = "tabulate", specifier = ">=0.9.0" }, + { name = "theseus", extras = ["cv", "cv-classification", "cv-semantic", "cv-detection", "nlp", "nlp-retrieval", "ml"], marker = "extra == 'all'" }, + { name = "timm", marker = "extra == 'cv-classification'", specifier = ">=1.0.0" }, + { name = "torch", specifier = ">=2.11.0", index = "https://download.pytorch.org/whl/cu126" }, + { name = "torchvision", specifier = ">=0.26.0", index = "https://download.pytorch.org/whl/cu126" }, + { name = "tqdm", specifier = ">=4.66.0" }, + { name = "transformers", marker = "extra == 'nlp'", specifier = ">=4.40.0" }, + { name = "wandb", specifier = ">=0.17.0" }, + { name = "webcolors", specifier = ">=1.13" }, + { name = "xgboost", marker = "extra == 'ml'", specifier = ">=3.0" }, +] +provides-extras = ["dev", "cv", "cv-classification", "cv-semantic", "cv-detection", "nlp", "nlp-retrieval", "ml", "all"] + +[[package]] +name = "thinc" +version = "8.3.10" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "blis" }, + { name = "catalogue" }, + { name = "confection" }, + { name = "cymem" }, + { name = "murmurhash" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "preshed" }, + { name = "pydantic" }, + { name = "setuptools" }, + { name = "srsly" }, + { name = "wasabi" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2f/3a/2d0f0be132b9faaa6d56f04565ae122684273e4bf4eab8dee5f48dc00f68/thinc-8.3.10.tar.gz", hash = "sha256:5a75109f4ee1c968fc055ce651a17cb44b23b000d9e95f04a4d047ab3cb3e34e", size = 194196, upload-time = "2025-11-17T17:21:46.435Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/bc/d3c364c0278e420e0e3d328cbae7cd7aac8d2cfe4d9b8022a12e99f03755/thinc-8.3.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:fbe0313cb3c898f4e6a3f13b704af51f4bf8f927078deb0fe2d6eaf3c6c5b31b", size = 821615, upload-time = "2025-11-17T17:20:31.257Z" }, + { url = "https://files.pythonhosted.org/packages/0e/97/70fe96d86fe5d024882fd96f054be94f87828da67862749aa439de33d452/thinc-8.3.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:892ac91cf7cc8d3ac9a4527c68ead37a96e87132c9f589de56b057b50358e895", size = 772280, upload-time = "2025-11-17T17:20:34.408Z" }, + { url = "https://files.pythonhosted.org/packages/08/a8/a6906490a756a4ad09781bcd02490e5427d942a918abed8424f639d317c3/thinc-8.3.10-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0fbf142050feb5490f6366e251d48e0429315abe487faa7d371fac4d043efd1e", size = 3881222, upload-time = "2025-11-17T17:20:36.525Z" }, + { url = "https://files.pythonhosted.org/packages/e6/bf/bebeddbab816c4d909455499f7e1b0a88cec9497fd737412e1189971d193/thinc-8.3.10-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:470b05fd1af4024cf183f387f71270943f652dd711304d1fa8b672d268052af8", size = 3905534, upload-time = "2025-11-17T17:20:38.901Z" }, + { url = "https://files.pythonhosted.org/packages/fd/c4/c78f1e1091b73dbeee8623f856e2dd25888aab600ded5fa9944dfbe38efb/thinc-8.3.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06ebf4aa642991b8dc5c2a6db4c0aedf6d5589a361c93531ec3721d76eabe859", size = 4888188, upload-time = "2025-11-17T17:20:41.394Z" }, + { url = "https://files.pythonhosted.org/packages/ca/bc/36297efade38e0f3e56795f49094d19fbe560bda60a42ce134bbfc1796da/thinc-8.3.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:026999d749075c890fbb1df47d75389a81b712afccea519a5c7bb86783d0cd73", size = 5033361, upload-time = "2025-11-17T17:20:45.332Z" }, + { url = "https://files.pythonhosted.org/packages/a8/bf/70d97758b5b1c7ee06afca8240b6e02bdf5b18d18eb59b873e319b3e01b2/thinc-8.3.10-cp310-cp310-win_amd64.whl", hash = "sha256:8d5ae7d96ff3ea2e4f23bd4005c773f4765f41b11dfb79598a81e5feb1437b91", size = 1792397, upload-time = "2025-11-17T17:20:47.014Z" }, + { url = "https://files.pythonhosted.org/packages/38/43/01b662540888140b5e9f76c957c7118c203cb91f17867ce78fc4f2d3800f/thinc-8.3.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:72793e0bd3f0f391ca36ab0996b3c21db7045409bd3740840e7d6fcd9a044d81", size = 818632, upload-time = "2025-11-17T17:20:49.123Z" }, + { url = "https://files.pythonhosted.org/packages/f0/ba/e0edcc84014bdde1bc9a082408279616a061566a82b5e3b90b9e64f33c1b/thinc-8.3.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4b13311acb061e04e3a0c4bd677b85ec2971e3a3674558252443b5446e378256", size = 770622, upload-time = "2025-11-17T17:20:50.467Z" }, + { url = "https://files.pythonhosted.org/packages/f3/51/0558f8cb69c13e1114428726a3fb36fe1adc5821a62ccd3fa7b7c1a5bd9a/thinc-8.3.10-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9ffddcf311fb7c998eb8988d22c618dc0f33b26303853c0445edb8a69819ac60", size = 4094652, upload-time = "2025-11-17T17:20:52.104Z" }, + { url = "https://files.pythonhosted.org/packages/a0/c9/bb78601f74f9bcadb2d3d4d5b057c4dc3f2e52d9771bad3d93a4e38a9dc1/thinc-8.3.10-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9b1e0511e8421f20abe4f22d8c8073a0d7ce4a31597cc7a404fdbad72bf38058", size = 4124379, upload-time = "2025-11-17T17:20:53.781Z" }, + { url = "https://files.pythonhosted.org/packages/f6/3e/961e1b9794111c89f2ceadfef5692aba5097bec4aaaf89f1b8a04c5bc961/thinc-8.3.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e31e49441dfad8fd64b8ca5f5c9b8c33ee87a553bf79c830a15b4cd02efcc444", size = 5094221, upload-time = "2025-11-17T17:20:55.466Z" }, + { url = "https://files.pythonhosted.org/packages/e5/de/da163a1533faaef5b17dd11dfb9ffd9fd5627dbef56e1160da6edbe1b224/thinc-8.3.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9de5dd73ce7135dcf41d68625d35cd9f5cf8e5f55a3932001a188b45057c3379", size = 5262834, upload-time = "2025-11-17T17:20:57.459Z" }, + { url = "https://files.pythonhosted.org/packages/4c/4e/449d29e33f7ddda6ba1b9e06de3ea5155c2dc33c21f438f8faafebde4e13/thinc-8.3.10-cp311-cp311-win_amd64.whl", hash = "sha256:b6d64e390a1996d489872b9d99a584142542aba59ebdc60f941f473732582f6f", size = 1791864, upload-time = "2025-11-17T17:20:59.817Z" }, + { url = "https://files.pythonhosted.org/packages/4a/b3/68038d88d45d83a501c3f19bd654d275b7ac730c807f52bbb46f35f591bc/thinc-8.3.10-cp311-cp311-win_arm64.whl", hash = "sha256:3991b6ad72e611dfbfb58235de5b67bcc9f61426127cc023607f97e8c5f43e0e", size = 1717563, upload-time = "2025-11-17T17:21:01.634Z" }, + { url = "https://files.pythonhosted.org/packages/d3/34/ba3b386d92edf50784b60ee34318d47c7f49c198268746ef7851c5bbe8cf/thinc-8.3.10-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:51bc6ef735bdbcab75ab2916731b8f61f94c66add6f9db213d900d3c6a244f95", size = 794509, upload-time = "2025-11-17T17:21:03.21Z" }, + { url = "https://files.pythonhosted.org/packages/07/f3/9f52d18115cd9d8d7b2590d226cb2752d2a5ffec61576b19462b48410184/thinc-8.3.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4f48b4d346915f98e9722c0c50ef911cc16c6790a2b7afebc6e1a2c96a6ce6c6", size = 741084, upload-time = "2025-11-17T17:21:04.568Z" }, + { url = "https://files.pythonhosted.org/packages/ad/9c/129c2b740c4e3d3624b6fb3dec1577ef27cb804bc1647f9bc3e1801ea20c/thinc-8.3.10-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5003f4db2db22cc8d686db8db83509acc3c50f4c55ebdcb2bbfcc1095096f7d2", size = 3846337, upload-time = "2025-11-17T17:21:06.079Z" }, + { url = "https://files.pythonhosted.org/packages/22/d2/738cf188dea8240c2be081c83ea47270fea585eba446171757d2cdb9b675/thinc-8.3.10-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b12484c3ed0632331fada2c334680dd6bc35972d0717343432dfc701f04a9b4c", size = 3901216, upload-time = "2025-11-17T17:21:07.842Z" }, + { url = "https://files.pythonhosted.org/packages/22/92/32f66eb9b1a29b797bf378a0874615d810d79eefca1d6c736c5ca3f8b918/thinc-8.3.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8677c446d3f9b97a465472c58683b785b25dfcf26c683e3f4e8f8c7c188e4362", size = 4827286, upload-time = "2025-11-17T17:21:09.62Z" }, + { url = "https://files.pythonhosted.org/packages/c4/5f/7ceae1e1f2029efd67ed88e23cd6dc13a5ee647cdc2b35113101b2a62c10/thinc-8.3.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:759c385ac08dcf950238b60b96a28f9c04618861141766928dff4a51b1679b25", size = 5024421, upload-time = "2025-11-17T17:21:11.199Z" }, + { url = "https://files.pythonhosted.org/packages/0b/66/30f9d8d41049b78bc614213d492792fbcfeb1b28642adf661c42110a7ebd/thinc-8.3.10-cp312-cp312-win_amd64.whl", hash = "sha256:bf3f188c3fa1fdcefd547d1f90a1245c29025d6d0e3f71d7fdf21dad210b990c", size = 1718631, upload-time = "2025-11-17T17:21:12.965Z" }, + { url = "https://files.pythonhosted.org/packages/f8/44/32e2a5018a1165a304d25eb9b1c74e5310da19a533a35331e8d824dc6a88/thinc-8.3.10-cp312-cp312-win_arm64.whl", hash = "sha256:234b7e57a6ef4e0260d99f4e8fdc328ed12d0ba9bbd98fdaa567294a17700d1c", size = 1642224, upload-time = "2025-11-17T17:21:14.371Z" }, +] + +[[package]] +name = "threadpoolctl" +version = "3.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b7/4d/08c89e34946fce2aec4fbb45c9016efd5f4d7f24af8e5d93296e935631d8/threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e", size = 21274, upload-time = "2025-03-13T13:49:23.031Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638, upload-time = "2025-03-13T13:49:21.846Z" }, +] + +[[package]] +name = "timm" +version = "1.0.22" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, + { name = "pyyaml" }, + { name = "safetensors" }, + { name = "torch" }, + { name = "torchvision" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c5/9d/e4670765d1c033f97096c760b3b907eeb659cf80f3678640e5f060b04c6c/timm-1.0.22.tar.gz", hash = "sha256:14fd74bcc17db3856b1a47d26fb305576c98579ab9d02b36714a5e6b25cde422", size = 2382998, upload-time = "2025-11-05T04:06:09.377Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d6/14/fc04d491527b774ec7479897f5861959209de1480e4c4cd32ed098ff8bea/timm-1.0.22-py3-none-any.whl", hash = "sha256:888981753e65cbaacfc07494370138b1700a27b1f0af587f4f9b47bc024161d0", size = 2530238, upload-time = "2025-11-05T04:06:06.823Z" }, +] + +[[package]] +name = "tokenizers" +version = "0.22.1" +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, upload-time = "2025-09-19T09:49:23.424Z" } +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, upload-time = "2025-09-19T09:49:11.848Z" }, + { 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, upload-time = "2025-09-19T09:49:09.759Z" }, + { 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, upload-time = "2025-09-19T09:48:56.701Z" }, + { 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, upload-time = "2025-09-19T09:48:59.749Z" }, + { 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, upload-time = "2025-09-19T09:49:05.868Z" }, + { 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, upload-time = "2025-09-19T09:49:01.832Z" }, + { 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, upload-time = "2025-09-19T09:49:03.867Z" }, + { 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, upload-time = "2025-09-19T09:49:07.664Z" }, + { 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, upload-time = "2025-09-19T09:49:14.214Z" }, + { 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, upload-time = "2025-09-19T09:49:16.639Z" }, + { 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, upload-time = "2025-09-19T09:49:19.146Z" }, + { 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, upload-time = "2025-09-19T09:49:21.501Z" }, + { url = "https://files.pythonhosted.org/packages/30/2c/959dddef581b46e6209da82df3b78471e96260e2bc463f89d23b1bf0e52a/tokenizers-0.22.1-cp39-abi3-win32.whl", hash = "sha256:b5120eed1442765cd90b903bb6cfef781fd8fe64e34ccaecbae4c619b7b12a82", size = 2472003, upload-time = "2025-09-19T09:49:27.089Z" }, + { url = "https://files.pythonhosted.org/packages/b3/46/e33a8c93907b631a99377ef4c5f817ab453d0b34f93529421f42ff559671/tokenizers-0.22.1-cp39-abi3-win_amd64.whl", hash = "sha256:65fd6e3fb11ca1e78a6a93602490f134d1fdeb13bcef99389d5102ea318ed138", size = 2674684, upload-time = "2025-09-19T09:49:24.953Z" }, +] + +[[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" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/2e/299f62b401438d5fe1624119c723f5d877acc86a4c2492da405626665f12/tomli-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:88bd15eb972f3664f5ed4b57c1634a97153b4bac4479dcb6a495f41921eb7f45", size = 153236, upload-time = "2025-10-08T22:01:00.137Z" }, + { url = "https://files.pythonhosted.org/packages/86/7f/d8fffe6a7aefdb61bced88fcb5e280cfd71e08939da5894161bd71bea022/tomli-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:883b1c0d6398a6a9d29b508c331fa56adbcdff647f6ace4dfca0f50e90dfd0ba", size = 148084, upload-time = "2025-10-08T22:01:01.63Z" }, + { url = "https://files.pythonhosted.org/packages/47/5c/24935fb6a2ee63e86d80e4d3b58b222dafaf438c416752c8b58537c8b89a/tomli-2.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1381caf13ab9f300e30dd8feadb3de072aeb86f1d34a8569453ff32a7dea4bf", size = 234832, upload-time = "2025-10-08T22:01:02.543Z" }, + { url = "https://files.pythonhosted.org/packages/89/da/75dfd804fc11e6612846758a23f13271b76d577e299592b4371a4ca4cd09/tomli-2.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0e285d2649b78c0d9027570d4da3425bdb49830a6156121360b3f8511ea3441", size = 242052, upload-time = "2025-10-08T22:01:03.836Z" }, + { url = "https://files.pythonhosted.org/packages/70/8c/f48ac899f7b3ca7eb13af73bacbc93aec37f9c954df3c08ad96991c8c373/tomli-2.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a154a9ae14bfcf5d8917a59b51ffd5a3ac1fd149b71b47a3a104ca4edcfa845", size = 239555, upload-time = "2025-10-08T22:01:04.834Z" }, + { url = "https://files.pythonhosted.org/packages/ba/28/72f8afd73f1d0e7829bfc093f4cb98ce0a40ffc0cc997009ee1ed94ba705/tomli-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:74bf8464ff93e413514fefd2be591c3b0b23231a77f901db1eb30d6f712fc42c", size = 245128, upload-time = "2025-10-08T22:01:05.84Z" }, + { url = "https://files.pythonhosted.org/packages/b6/eb/a7679c8ac85208706d27436e8d421dfa39d4c914dcf5fa8083a9305f58d9/tomli-2.3.0-cp311-cp311-win32.whl", hash = "sha256:00b5f5d95bbfc7d12f91ad8c593a1659b6387b43f054104cda404be6bda62456", size = 96445, upload-time = "2025-10-08T22:01:06.896Z" }, + { url = "https://files.pythonhosted.org/packages/0a/fe/3d3420c4cb1ad9cb462fb52967080575f15898da97e21cb6f1361d505383/tomli-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:4dc4ce8483a5d429ab602f111a93a6ab1ed425eae3122032db7e9acf449451be", size = 107165, upload-time = "2025-10-08T22:01:08.107Z" }, + { url = "https://files.pythonhosted.org/packages/ff/b7/40f36368fcabc518bb11c8f06379a0fd631985046c038aca08c6d6a43c6e/tomli-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d7d86942e56ded512a594786a5ba0a5e521d02529b3826e7761a05138341a2ac", size = 154891, upload-time = "2025-10-08T22:01:09.082Z" }, + { url = "https://files.pythonhosted.org/packages/f9/3f/d9dd692199e3b3aab2e4e4dd948abd0f790d9ded8cd10cbaae276a898434/tomli-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:73ee0b47d4dad1c5e996e3cd33b8a76a50167ae5f96a2607cbe8cc773506ab22", size = 148796, upload-time = "2025-10-08T22:01:10.266Z" }, + { url = "https://files.pythonhosted.org/packages/60/83/59bff4996c2cf9f9387a0f5a3394629c7efa5ef16142076a23a90f1955fa/tomli-2.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:792262b94d5d0a466afb5bc63c7daa9d75520110971ee269152083270998316f", size = 242121, upload-time = "2025-10-08T22:01:11.332Z" }, + { url = "https://files.pythonhosted.org/packages/45/e5/7c5119ff39de8693d6baab6c0b6dcb556d192c165596e9fc231ea1052041/tomli-2.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f195fe57ecceac95a66a75ac24d9d5fbc98ef0962e09b2eddec5d39375aae52", size = 250070, upload-time = "2025-10-08T22:01:12.498Z" }, + { url = "https://files.pythonhosted.org/packages/45/12/ad5126d3a278f27e6701abde51d342aa78d06e27ce2bb596a01f7709a5a2/tomli-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e31d432427dcbf4d86958c184b9bfd1e96b5b71f8eb17e6d02531f434fd335b8", size = 245859, upload-time = "2025-10-08T22:01:13.551Z" }, + { url = "https://files.pythonhosted.org/packages/fb/a1/4d6865da6a71c603cfe6ad0e6556c73c76548557a8d658f9e3b142df245f/tomli-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b0882799624980785240ab732537fcfc372601015c00f7fc367c55308c186f6", size = 250296, upload-time = "2025-10-08T22:01:14.614Z" }, + { url = "https://files.pythonhosted.org/packages/a0/b7/a7a7042715d55c9ba6e8b196d65d2cb662578b4d8cd17d882d45322b0d78/tomli-2.3.0-cp312-cp312-win32.whl", hash = "sha256:ff72b71b5d10d22ecb084d345fc26f42b5143c5533db5e2eaba7d2d335358876", size = 97124, upload-time = "2025-10-08T22:01:15.629Z" }, + { url = "https://files.pythonhosted.org/packages/06/1e/f22f100db15a68b520664eb3328fb0ae4e90530887928558112c8d1f4515/tomli-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:1cb4ed918939151a03f33d4242ccd0aa5f11b3547d0cf30f7c74a408a5b99878", size = 107698, upload-time = "2025-10-08T22:01:16.51Z" }, + { 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]] +name = "torch" +version = "2.11.0+cu126" +source = { registry = "https://download.pytorch.org/whl/cu126" } +dependencies = [ + { name = "cuda-bindings", marker = "sys_platform == 'linux'" }, + { name = "cuda-toolkit", extra = ["cublas", "cudart", "cufft", "cufile", "cupti", "curand", "cusolver", "cusparse", "nvjitlink", "nvrtc", "nvtx"], marker = "sys_platform == 'linux'" }, + { name = "filelock" }, + { name = "fsspec" }, + { name = "jinja2" }, + { name = "networkx", version = "3.4.2", source = { registry = "https://download.pytorch.org/whl/cu126" }, marker = "python_full_version < '3.11'" }, + { name = "networkx", version = "3.6", source = { registry = "https://download.pytorch.org/whl/cu126" }, marker = "python_full_version >= '3.11'" }, + { name = "nvidia-cudnn-cu12", marker = "sys_platform == 'linux'" }, + { name = "nvidia-cusparselt-cu12", marker = "sys_platform == 'linux'" }, + { name = "nvidia-nccl-cu12", marker = "sys_platform == 'linux'" }, + { name = "nvidia-nvshmem-cu12", marker = "sys_platform == 'linux'" }, + { name = "setuptools" }, + { name = "sympy" }, + { name = "triton", marker = "sys_platform == 'linux'" }, + { name = "typing-extensions" }, +] +wheels = [ + { url = "https://download.pytorch.org/whl/cu126/torch-2.11.0%2Bcu126-cp310-cp310-manylinux_2_28_aarch64.whl" }, + { url = "https://download.pytorch.org/whl/cu126/torch-2.11.0%2Bcu126-cp310-cp310-manylinux_2_28_x86_64.whl" }, + { url = "https://download.pytorch.org/whl/cu126/torch-2.11.0%2Bcu126-cp310-cp310-win_amd64.whl" }, + { url = "https://download.pytorch.org/whl/cu126/torch-2.11.0%2Bcu126-cp311-cp311-manylinux_2_28_aarch64.whl" }, + { url = "https://download.pytorch.org/whl/cu126/torch-2.11.0%2Bcu126-cp311-cp311-manylinux_2_28_x86_64.whl" }, + { url = "https://download.pytorch.org/whl/cu126/torch-2.11.0%2Bcu126-cp311-cp311-win_amd64.whl" }, + { url = "https://download.pytorch.org/whl/cu126/torch-2.11.0%2Bcu126-cp312-cp312-manylinux_2_28_aarch64.whl" }, + { url = "https://download.pytorch.org/whl/cu126/torch-2.11.0%2Bcu126-cp312-cp312-manylinux_2_28_x86_64.whl" }, + { url = "https://download.pytorch.org/whl/cu126/torch-2.11.0%2Bcu126-cp312-cp312-win_amd64.whl" }, +] + +[[package]] +name = "torchmetrics" +version = "1.0.3" +source = { registry = "https://download.pytorch.org/whl/cu126" } +dependencies = [ + { name = "lightning-utilities" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "torch" }, +] +wheels = [ + { url = "https://download.pytorch.org/whl/torchmetrics-1.0.3-py3-none-any.whl", hash = "sha256:612a74ab8ebfcd4ebb38e5c370ce29a0e73af074948048f6f2233e25cf60da75" }, +] + +[[package]] +name = "torchvision" +version = "0.26.0+cu126" +source = { registry = "https://download.pytorch.org/whl/cu126" } +dependencies = [ + { name = "numpy" }, + { name = "pillow" }, + { name = "torch" }, +] +wheels = [ + { url = "https://download-r2.pytorch.org/whl/cu126/torchvision-0.26.0%2Bcu126-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:f00219ac240be76ac7693dc0459d19d35b3a6bc778aeb461d70fa24a78602f14" }, + { url = "https://download-r2.pytorch.org/whl/cu126/torchvision-0.26.0%2Bcu126-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:8af0ce71fb0c8773daa16443ac5b65f21a0b18a501bcf4fc7750c03a76bfc9ef" }, + { url = "https://download-r2.pytorch.org/whl/cu126/torchvision-0.26.0%2Bcu126-cp310-cp310-win_amd64.whl", hash = "sha256:233a63229f773ad63ba9f6ea4713d905839aecd58291a169b1bcee7c29356029" }, + { url = "https://download-r2.pytorch.org/whl/cu126/torchvision-0.26.0%2Bcu126-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:9a197ed1684633b95a49f89cc860bbff763674d737f5ba5100be74caa98acd9f" }, + { url = "https://download-r2.pytorch.org/whl/cu126/torchvision-0.26.0%2Bcu126-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:26bc03574a5e1d3b48348292d523e089db8e833fac2f56a49ebbfde571b90db2" }, + { url = "https://download-r2.pytorch.org/whl/cu126/torchvision-0.26.0%2Bcu126-cp311-cp311-win_amd64.whl" }, + { url = "https://download-r2.pytorch.org/whl/cu126/torchvision-0.26.0%2Bcu126-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:b9422be894e6969bcd33f20e0a1599c5e7d43b39ef548f35d3f83557053fd1a4" }, + { url = "https://download-r2.pytorch.org/whl/cu126/torchvision-0.26.0%2Bcu126-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:a127341237ec2e70a54d031d2bae7be395f5d24c5a7c3e8b8b03cdd1fcebcb53" }, + { url = "https://download-r2.pytorch.org/whl/cu126/torchvision-0.26.0%2Bcu126-cp312-cp312-win_amd64.whl" }, +] + +[[package]] +name = "tqdm" +version = "4.66.5" +source = { registry = "https://download.pytorch.org/whl/cu126" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +wheels = [ + { url = "https://download.pytorch.org/whl/tqdm-4.66.5-py3-none-any.whl", hash = "sha256:90279a3770753eafc9194a0364852159802111925aa30eb3f9d85b0e805ac7cd" }, +] + +[[package]] +name = "transformers" +version = "4.57.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "huggingface-hub" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "regex" }, + { name = "requests" }, + { name = "safetensors" }, + { 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" } +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" }, +] + +[[package]] +name = "triton" +version = "3.6.0" +source = { registry = "https://download.pytorch.org/whl/cu126" } +wheels = [ + { url = "https://download.pytorch.org/whl/triton-3.6.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2d0b7b9b4f29ade31cf7bd7c237b50d270a2487cf943d034b307a9b3f73d9c7" }, + { url = "https://download.pytorch.org/whl/triton-3.6.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:72d6b3eeaa13444ca29ba65d55fc57b6411373a2b8e619f63ae5962d3d9ea249" }, + { url = "https://download.pytorch.org/whl/triton-3.6.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:290687f4f310ce794a07d6a43fa94dea9bda86615bf04578533378503bed715a" }, + { url = "https://download.pytorch.org/whl/triton-3.6.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e021e87e8f266c6f87bf379b669c43c2800aac6247bdf5d518cb553e3c403c00" }, + { url = "https://download.pytorch.org/whl/triton-3.6.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e42d084864d12b8784736fe51a0cd05f6cc56775b25c8102c9d80c421f4e298" }, + { url = "https://download.pytorch.org/whl/triton-3.6.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f5928e6d44c34a97bbe164cceddc0ef2007121c89ebcfba5415cf452de7ee9f" }, +] + +[[package]] +name = "ttach" +version = "0.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/91/5d/4c49e0eca4206bc25eff4ba89cee51b781466e2e3aad2f1057fd5d2634be/ttach-0.0.3.tar.gz", hash = "sha256:120c4dd881feb0e9c8dd63b154f2655891c3e20689b68a94d162bfd5557bcb48", size = 9600, upload-time = "2020-07-09T14:44:09.035Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/a3/ee48a184a185c1897c582c72240c2c8a0d0aeb5f8051a71d4e4cd930c52d/ttach-0.0.3-py3-none-any.whl", hash = "sha256:7000bb4334f856b0c79a341df386c92f1c76faf091043cc3cd7f541d2149faf8", size = 9839, upload-time = "2020-07-09T14:44:08.006Z" }, +] + +[[package]] +name = "typer-slim" +version = "0.20.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8e/45/81b94a52caed434b94da65729c03ad0fb7665fab0f7db9ee54c94e541403/typer_slim-0.20.0.tar.gz", hash = "sha256:9fc6607b3c6c20f5c33ea9590cbeb17848667c51feee27d9e314a579ab07d1a3", size = 106561, upload-time = "2025-10-20T17:03:46.642Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/dd/5cbf31f402f1cc0ab087c94d4669cfa55bd1e818688b910631e131d74e75/typer_slim-0.20.0-py3-none-any.whl", hash = "sha256:f42a9b7571a12b97dddf364745d29f12221865acef7a2680065f9bb29c7dc89d", size = 47087, upload-time = "2025-10-20T17:03:44.546Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://download.pytorch.org/whl/cu126" } +wheels = [ + { url = "https://download.pytorch.org/whl/typing_extensions-4.15.0-py3-none-any.whl" }, +] + +[[package]] +name = "typing-inspection" +version = "0.4.2" +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" } +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" }, +] + +[[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, upload-time = "2025-03-23T13:54:43.652Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839, upload-time = "2025-03-23T13:54:41.845Z" }, +] + +[[package]] +name = "urllib3" +version = "1.26.13" +source = { registry = "https://download.pytorch.org/whl/cu126" } +wheels = [ + { url = "https://download.pytorch.org/whl/urllib3-1.26.13-py2.py3-none-any.whl", hash = "sha256:47cc05d99aaa09c9e72ed5809b60e7ba354e64b59c9c173ac3018642d8bb41fc" }, +] + +[[package]] +name = "virtualenv" +version = "20.35.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "distlib" }, + { name = "filelock" }, + { name = "platformdirs" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +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, upload-time = "2025-10-29T06:57:37.598Z" }, +] + +[[package]] +name = "wandb" +version = "0.19.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "docker-pycreds" }, + { name = "gitpython" }, + { name = "platformdirs" }, + { name = "protobuf" }, + { name = "psutil" }, + { name = "pydantic" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "sentry-sdk" }, + { name = "setproctitle" }, + { name = "setuptools" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5f/07/c4f4ace4445cfafcd6a35dff94e03b3b875f2e472ae524c1d32fec8a5c33/wandb-0.19.9.tar.gz", hash = "sha256:a38881aa7770e6ea0f792268250e5b888a245c2e4829e5830ce08f5fa95a2ad1", size = 39264462, upload-time = "2025-04-01T21:22:05.475Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ad/32/39de0bcf45f0026aabbc9dcd41c287dc21f62b5cb6fe054bbb3b988ff01c/wandb-0.19.9-py3-none-any.whl", hash = "sha256:191b2c794b401bd04cc7c48b16339ca2832510af8499348744e7461a1609b6a9", size = 6335646, upload-time = "2025-04-01T21:21:39.799Z" }, + { url = "https://files.pythonhosted.org/packages/c6/61/f37280987c4ef624e238be4cbad4a0ffe71987fbb75c029fc89c6bd849b7/wandb-0.19.9-py3-none-macosx_10_14_x86_64.whl", hash = "sha256:de5702416f29d1d61f9e85f4090346f23db1ee7f23cab37a4dd5d394fcf5f7da", size = 20468027, upload-time = "2025-04-01T21:21:42.422Z" }, + { url = "https://files.pythonhosted.org/packages/49/35/7af15563f7cc40c7ac95f2d2e5ca10fa144d1be20a62da8f475cc7ceebd3/wandb-0.19.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c598041f06be3cbed6bf6888cb67b033352ce156dc4e7882c4448e2750453a77", size = 19931553, upload-time = "2025-04-01T21:21:45.041Z" }, + { url = "https://files.pythonhosted.org/packages/f9/b2/aa02811d0eb66530e67c44cc1e7d001507064d54b036f2a801bc9c0a731f/wandb-0.19.9-py3-none-macosx_11_0_x86_64.whl", hash = "sha256:7b99253c1f603d9331fa0626df61f47f0b544403321286c4fff50492572738f5", size = 20466642, upload-time = "2025-04-01T21:21:47.568Z" }, + { url = "https://files.pythonhosted.org/packages/56/07/47ab3b4f0f4a32d9269ecb60aa71da3e426faa2abe51c4f000778e2696c3/wandb-0.19.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a074ad070c4e8cbb03b2149a98abbe2d7562220f095a21c736e1abbca399eef", size = 19534405, upload-time = "2025-04-01T21:21:50.313Z" }, + { url = "https://files.pythonhosted.org/packages/89/d0/737d26d709bd7bc3f6b2250f41fda3d0787239cfdbd6eb13057c64c81ace/wandb-0.19.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5dc6c7180a5bf1eb5bd9cab8a1886fd980c76d54253c967082fe19d197443a2d", size = 20863142, upload-time = "2025-04-01T21:21:52.462Z" }, + { url = "https://files.pythonhosted.org/packages/28/a5/420515e3f724d730ef830e2362aaa53ce49f366d919fbfa9ed4c3a148095/wandb-0.19.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1e8cea987f326b053e79b7c40a0502c61a23792c9869b92ab6f2ac76086f66c2", size = 19545396, upload-time = "2025-04-01T21:21:54.707Z" }, + { url = "https://files.pythonhosted.org/packages/56/05/bab99791ede9eb78d03ddd27c54cbafcd89db1f279d4f4634156ebfcc24f/wandb-0.19.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:969a700cd625f56b2ac6c9e136ae828a0c3b2ebc4dc289c829986e9e7aded6bb", size = 20944223, upload-time = "2025-04-01T21:21:57.552Z" }, + { url = "https://files.pythonhosted.org/packages/07/ab/e3f728ff75307ba387546d3aedba7f18b4f5467d92d0343df2dee75af27e/wandb-0.19.9-py3-none-win32.whl", hash = "sha256:d3a192fec8fdfd89993243a81cad332c6c0e7127a9497b6bf9c244adeab2d717", size = 20247779, upload-time = "2025-04-01T21:22:00.265Z" }, + { url = "https://files.pythonhosted.org/packages/78/89/fee9e1513cc7045e9a6235b14d8e6e5b625fe4fe3a281fda9e799c6269ed/wandb-0.19.9-py3-none-win_amd64.whl", hash = "sha256:2033dacee312988e5418d0f257fd9888d28b6785ec39ec43cb2f6b8fd2f13176", size = 20247782, upload-time = "2025-04-01T21:22:02.983Z" }, +] + +[[package]] +name = "wasabi" +version = "1.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ac/f9/054e6e2f1071e963b5e746b48d1e3727470b2a490834d18ad92364929db3/wasabi-1.1.3.tar.gz", hash = "sha256:4bb3008f003809db0c3e28b4daf20906ea871a2bb43f9914197d540f4f2e0878", size = 30391, upload-time = "2024-05-31T16:56:18.99Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/06/7c/34330a89da55610daa5f245ddce5aab81244321101614751e7537f125133/wasabi-1.1.3-py3-none-any.whl", hash = "sha256:f76e16e8f7e79f8c4c8be49b4024ac725713ab10cd7f19350ad18a8e3f71728c", size = 27880, upload-time = "2024-05-31T16:56:16.699Z" }, +] + +[[package]] +name = "weasel" +version = "0.4.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cloudpathlib" }, + { name = "confection" }, + { name = "packaging" }, + { name = "pydantic" }, + { name = "requests" }, + { name = "smart-open" }, + { name = "srsly" }, + { name = "typer-slim" }, + { name = "wasabi" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/09/d7/edd9c24e60cf8e5de130aa2e8af3b01521f4d0216c371d01212f580d0d8e/weasel-0.4.3.tar.gz", hash = "sha256:f293d6174398e8f478c78481e00c503ee4b82ea7a3e6d0d6a01e46a6b1396845", size = 38733, upload-time = "2025-11-13T23:52:28.193Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/74/a148b41572656904a39dfcfed3f84dd1066014eed94e209223ae8e9d088d/weasel-0.4.3-py3-none-any.whl", hash = "sha256:08f65b5d0dbded4879e08a64882de9b9514753d9eaa4c4e2a576e33666ac12cf", size = 50757, upload-time = "2025-11-13T23:52:26.982Z" }, +] + +[[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" } +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" }, +] + +[[package]] +name = "win32-setctime" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b3/8f/705086c9d734d3b663af0e9bb3d4de6578d08f46b1b101c2442fd9aecaa2/win32_setctime-1.2.0.tar.gz", hash = "sha256:ae1fdf948f5640aae05c511ade119313fb6a30d7eabe25fef9764dca5873c4c0", size = 4867, upload-time = "2024-12-07T15:28:28.314Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/07/c6fe3ad3e685340704d314d765b7912993bcb8dc198f0e7a89382d37974b/win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390", size = 4083, upload-time = "2024-12-07T15:28:26.465Z" }, +] + +[[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" } +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/98/60/553997acf3939079dab022e37b67b1904b5b0cc235503226898ba573b10c/wrapt-2.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0e17283f533a0d24d6e5429a7d11f250a58d28b4ae5186f8f47853e3e70d2590", size = 77480, upload-time = "2025-11-07T00:43:30.573Z" }, + { url = "https://files.pythonhosted.org/packages/2d/50/e5b3d30895d77c52105c6d5cbf94d5b38e2a3dd4a53d22d246670da98f7c/wrapt-2.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:85df8d92158cb8f3965aecc27cf821461bb5f40b450b03facc5d9f0d4d6ddec6", size = 60690, upload-time = "2025-11-07T00:43:31.594Z" }, + { url = "https://files.pythonhosted.org/packages/f0/40/660b2898703e5cbbb43db10cdefcc294274458c3ca4c68637c2b99371507/wrapt-2.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c1be685ac7700c966b8610ccc63c3187a72e33cab53526a27b2a285a662cd4f7", size = 61578, upload-time = "2025-11-07T00:43:32.918Z" }, + { url = "https://files.pythonhosted.org/packages/5b/36/825b44c8a10556957bc0c1d84c7b29a40e05fcf1873b6c40aa9dbe0bd972/wrapt-2.0.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:df0b6d3b95932809c5b3fecc18fda0f1e07452d05e2662a0b35548985f256e28", size = 114115, upload-time = "2025-11-07T00:43:35.605Z" }, + { url = "https://files.pythonhosted.org/packages/83/73/0a5d14bb1599677304d3c613a55457d34c344e9b60eda8a737c2ead7619e/wrapt-2.0.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4da7384b0e5d4cae05c97cd6f94faaf78cc8b0f791fc63af43436d98c4ab37bb", size = 116157, upload-time = "2025-11-07T00:43:37.058Z" }, + { url = "https://files.pythonhosted.org/packages/01/22/1c158fe763dbf0a119f985d945711d288994fe5514c0646ebe0eb18b016d/wrapt-2.0.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ec65a78fbd9d6f083a15d7613b2800d5663dbb6bb96003899c834beaa68b242c", size = 112535, upload-time = "2025-11-07T00:43:34.138Z" }, + { url = "https://files.pythonhosted.org/packages/5c/28/4f16861af67d6de4eae9927799b559c20ebdd4fe432e89ea7fe6fcd9d709/wrapt-2.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7de3cc939be0e1174969f943f3b44e0d79b6f9a82198133a5b7fc6cc92882f16", size = 115404, upload-time = "2025-11-07T00:43:39.214Z" }, + { url = "https://files.pythonhosted.org/packages/a0/8b/7960122e625fad908f189b59c4aae2d50916eb4098b0fb2819c5a177414f/wrapt-2.0.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:fb1a5b72cbd751813adc02ef01ada0b0d05d3dcbc32976ce189a1279d80ad4a2", size = 111802, upload-time = "2025-11-07T00:43:40.476Z" }, + { url = "https://files.pythonhosted.org/packages/3e/73/7881eee5ac31132a713ab19a22c9e5f1f7365c8b1df50abba5d45b781312/wrapt-2.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3fa272ca34332581e00bf7773e993d4f632594eb2d1b0b162a9038df0fd971dd", size = 113837, upload-time = "2025-11-07T00:43:42.921Z" }, + { url = "https://files.pythonhosted.org/packages/45/00/9499a3d14e636d1f7089339f96c4409bbc7544d0889f12264efa25502ae8/wrapt-2.0.1-cp311-cp311-win32.whl", hash = "sha256:fc007fdf480c77301ab1afdbb6ab22a5deee8885f3b1ed7afcb7e5e84a0e27be", size = 58028, upload-time = "2025-11-07T00:43:47.369Z" }, + { url = "https://files.pythonhosted.org/packages/70/5d/8f3d7eea52f22638748f74b102e38fdf88cb57d08ddeb7827c476a20b01b/wrapt-2.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:47434236c396d04875180171ee1f3815ca1eada05e24a1ee99546320d54d1d1b", size = 60385, upload-time = "2025-11-07T00:43:44.34Z" }, + { url = "https://files.pythonhosted.org/packages/14/e2/32195e57a8209003587bbbad44d5922f13e0ced2a493bb46ca882c5b123d/wrapt-2.0.1-cp311-cp311-win_arm64.whl", hash = "sha256:837e31620e06b16030b1d126ed78e9383815cbac914693f54926d816d35d8edf", size = 58893, upload-time = "2025-11-07T00:43:46.161Z" }, + { url = "https://files.pythonhosted.org/packages/cb/73/8cb252858dc8254baa0ce58ce382858e3a1cf616acebc497cb13374c95c6/wrapt-2.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1fdbb34da15450f2b1d735a0e969c24bdb8d8924892380126e2a293d9902078c", size = 78129, upload-time = "2025-11-07T00:43:48.852Z" }, + { url = "https://files.pythonhosted.org/packages/19/42/44a0db2108526ee6e17a5ab72478061158f34b08b793df251d9fbb9a7eb4/wrapt-2.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3d32794fe940b7000f0519904e247f902f0149edbe6316c710a8562fb6738841", size = 61205, upload-time = "2025-11-07T00:43:50.402Z" }, + { url = "https://files.pythonhosted.org/packages/4d/8a/5b4b1e44b791c22046e90d9b175f9a7581a8cc7a0debbb930f81e6ae8e25/wrapt-2.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:386fb54d9cd903ee0012c09291336469eb7b244f7183d40dc3e86a16a4bace62", size = 61692, upload-time = "2025-11-07T00:43:51.678Z" }, + { url = "https://files.pythonhosted.org/packages/11/53/3e794346c39f462bcf1f58ac0487ff9bdad02f9b6d5ee2dc84c72e0243b2/wrapt-2.0.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7b219cb2182f230676308cdcacd428fa837987b89e4b7c5c9025088b8a6c9faf", size = 121492, upload-time = "2025-11-07T00:43:55.017Z" }, + { url = "https://files.pythonhosted.org/packages/c6/7e/10b7b0e8841e684c8ca76b462a9091c45d62e8f2de9c4b1390b690eadf16/wrapt-2.0.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:641e94e789b5f6b4822bb8d8ebbdfc10f4e4eae7756d648b717d980f657a9eb9", size = 123064, upload-time = "2025-11-07T00:43:56.323Z" }, + { url = "https://files.pythonhosted.org/packages/0e/d1/3c1e4321fc2f5ee7fd866b2d822aa89b84495f28676fd976c47327c5b6aa/wrapt-2.0.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fe21b118b9f58859b5ebaa4b130dee18669df4bd111daad082b7beb8799ad16b", size = 117403, upload-time = "2025-11-07T00:43:53.258Z" }, + { url = "https://files.pythonhosted.org/packages/a4/b0/d2f0a413cf201c8c2466de08414a15420a25aa83f53e647b7255cc2fab5d/wrapt-2.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:17fb85fa4abc26a5184d93b3efd2dcc14deb4b09edcdb3535a536ad34f0b4dba", size = 121500, upload-time = "2025-11-07T00:43:57.468Z" }, + { url = "https://files.pythonhosted.org/packages/bd/45/bddb11d28ca39970a41ed48a26d210505120f925918592283369219f83cc/wrapt-2.0.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:b89ef9223d665ab255ae42cc282d27d69704d94be0deffc8b9d919179a609684", size = 116299, upload-time = "2025-11-07T00:43:58.877Z" }, + { url = "https://files.pythonhosted.org/packages/81/af/34ba6dd570ef7a534e7eec0c25e2615c355602c52aba59413411c025a0cb/wrapt-2.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a453257f19c31b31ba593c30d997d6e5be39e3b5ad9148c2af5a7314061c63eb", size = 120622, upload-time = "2025-11-07T00:43:59.962Z" }, + { url = "https://files.pythonhosted.org/packages/e2/3e/693a13b4146646fb03254636f8bafd20c621955d27d65b15de07ab886187/wrapt-2.0.1-cp312-cp312-win32.whl", hash = "sha256:3e271346f01e9c8b1130a6a3b0e11908049fe5be2d365a5f402778049147e7e9", size = 58246, upload-time = "2025-11-07T00:44:03.169Z" }, + { url = "https://files.pythonhosted.org/packages/a7/36/715ec5076f925a6be95f37917b66ebbeaa1372d1862c2ccd7a751574b068/wrapt-2.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:2da620b31a90cdefa9cd0c2b661882329e2e19d1d7b9b920189956b76c564d75", size = 60492, upload-time = "2025-11-07T00:44:01.027Z" }, + { url = "https://files.pythonhosted.org/packages/ef/3e/62451cd7d80f65cc125f2b426b25fbb6c514bf6f7011a0c3904fc8c8df90/wrapt-2.0.1-cp312-cp312-win_arm64.whl", hash = "sha256:aea9c7224c302bc8bfc892b908537f56c430802560e827b75ecbde81b604598b", size = 58987, upload-time = "2025-11-07T00:44:02.095Z" }, + { 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 = "xgboost" +version = "3.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "nvidia-nccl-cu12", marker = "sys_platform == 'linux'" }, + { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scipy", version = "1.16.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/91/bb/1eb0242409d22db725d7a88088e6cfd6556829fb0736f9ff69aa9f1e9455/xgboost-3.2.0.tar.gz", hash = "sha256:99b0e9a2a64896cdaf509c5e46372d336c692406646d20f2af505003c0c5d70d", size = 1263936, upload-time = "2026-02-10T11:03:05.542Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2d/49/6e4cdd877c24adf56cb3586bc96d93d4dcd780b5ea1efb32e1ee0de08bae/xgboost-3.2.0-py3-none-macosx_10_15_x86_64.whl", hash = "sha256:2f661966d3e322536d9c448090a870fcba1e32ee5760c10b7c46bac7a342079a", size = 2507014, upload-time = "2026-02-10T10:50:57.44Z" }, + { url = "https://files.pythonhosted.org/packages/93/f1/c09ef1add609453aa3ba5bafcd0d1c1a805c1263c0b60138ec968f8ec296/xgboost-3.2.0-py3-none-macosx_12_0_arm64.whl", hash = "sha256:eabbd40d474b8dbf6cb3536325f9150b9e6f0db32d18de9914fb3227d0bef5b7", size = 2328527, upload-time = "2026-02-10T10:51:17.502Z" }, + { url = "https://files.pythonhosted.org/packages/96/9f/d9914a7b8df842832850b1a18e5f47aaa071c217cdd1da2ae9deb291018b/xgboost-3.2.0-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:852eabc6d3b3702a59bf78dbfdcd1cb9c4d3a3b6e5ed1f8781d8b9512354fdd2", size = 131100954, upload-time = "2026-02-10T11:02:42.704Z" }, + { url = "https://files.pythonhosted.org/packages/79/98/679de17c2caa4fd3b0b4386ecf7377301702cb0afb22930a07c142fcb1d8/xgboost-3.2.0-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:99b4a6bbcb47212fec5cf5fbe12347215f073c08967431b0122cfbd1ee70312c", size = 131748579, upload-time = "2026-02-10T10:54:40.424Z" }, + { url = "https://files.pythonhosted.org/packages/1f/3d/1661dd114a914a67e3f7ab66fa1382e7599c2a8c340f314ad30a3e2b4d08/xgboost-3.2.0-py3-none-win_amd64.whl", hash = "sha256:0d169736fd836fc13646c7ab787167b3a8110351c2c6bc770c755ee1618f0442", size = 101681668, upload-time = "2026-02-10T10:59:31.202Z" }, +] + +[[package]] +name = "yarl" +version = "1.22.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, 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/4d/27/5ab13fc84c76a0250afd3d26d5936349a35be56ce5785447d6c423b26d92/yarl-1.22.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ab72135b1f2db3fed3997d7e7dc1b80573c67138023852b6efb336a5eae6511", size = 141607, upload-time = "2025-10-06T14:09:16.298Z" }, + { url = "https://files.pythonhosted.org/packages/6a/a1/d065d51d02dc02ce81501d476b9ed2229d9a990818332242a882d5d60340/yarl-1.22.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:669930400e375570189492dc8d8341301578e8493aec04aebc20d4717f899dd6", size = 94027, upload-time = "2025-10-06T14:09:17.786Z" }, + { url = "https://files.pythonhosted.org/packages/c1/da/8da9f6a53f67b5106ffe902c6fa0164e10398d4e150d85838b82f424072a/yarl-1.22.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:792a2af6d58177ef7c19cbf0097aba92ca1b9cb3ffdd9c7470e156c8f9b5e028", size = 94963, upload-time = "2025-10-06T14:09:19.662Z" }, + { url = "https://files.pythonhosted.org/packages/68/fe/2c1f674960c376e29cb0bec1249b117d11738db92a6ccc4a530b972648db/yarl-1.22.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ea66b1c11c9150f1372f69afb6b8116f2dd7286f38e14ea71a44eee9ec51b9d", size = 368406, upload-time = "2025-10-06T14:09:21.402Z" }, + { url = "https://files.pythonhosted.org/packages/95/26/812a540e1c3c6418fec60e9bbd38e871eaba9545e94fa5eff8f4a8e28e1e/yarl-1.22.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3e2daa88dc91870215961e96a039ec73e4937da13cf77ce17f9cad0c18df3503", size = 336581, upload-time = "2025-10-06T14:09:22.98Z" }, + { url = "https://files.pythonhosted.org/packages/0b/f5/5777b19e26fdf98563985e481f8be3d8a39f8734147a6ebf459d0dab5a6b/yarl-1.22.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ba440ae430c00eee41509353628600212112cd5018d5def7e9b05ea7ac34eb65", size = 388924, upload-time = "2025-10-06T14:09:24.655Z" }, + { url = "https://files.pythonhosted.org/packages/86/08/24bd2477bd59c0bbd994fe1d93b126e0472e4e3df5a96a277b0a55309e89/yarl-1.22.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e6438cc8f23a9c1478633d216b16104a586b9761db62bfacb6425bac0a36679e", size = 392890, upload-time = "2025-10-06T14:09:26.617Z" }, + { url = "https://files.pythonhosted.org/packages/46/00/71b90ed48e895667ecfb1eaab27c1523ee2fa217433ed77a73b13205ca4b/yarl-1.22.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c52a6e78aef5cf47a98ef8e934755abf53953379b7d53e68b15ff4420e6683d", size = 365819, upload-time = "2025-10-06T14:09:28.544Z" }, + { url = "https://files.pythonhosted.org/packages/30/2d/f715501cae832651d3282387c6a9236cd26bd00d0ff1e404b3dc52447884/yarl-1.22.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3b06bcadaac49c70f4c88af4ffcfbe3dc155aab3163e75777818092478bcbbe7", size = 363601, upload-time = "2025-10-06T14:09:30.568Z" }, + { url = "https://files.pythonhosted.org/packages/f8/f9/a678c992d78e394e7126ee0b0e4e71bd2775e4334d00a9278c06a6cce96a/yarl-1.22.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:6944b2dc72c4d7f7052683487e3677456050ff77fcf5e6204e98caf785ad1967", size = 358072, upload-time = "2025-10-06T14:09:32.528Z" }, + { url = "https://files.pythonhosted.org/packages/2c/d1/b49454411a60edb6fefdcad4f8e6dbba7d8019e3a508a1c5836cba6d0781/yarl-1.22.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d5372ca1df0f91a86b047d1277c2aaf1edb32d78bbcefffc81b40ffd18f027ed", size = 385311, upload-time = "2025-10-06T14:09:34.634Z" }, + { url = "https://files.pythonhosted.org/packages/87/e5/40d7a94debb8448c7771a916d1861d6609dddf7958dc381117e7ba36d9e8/yarl-1.22.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:51af598701f5299012b8416486b40fceef8c26fc87dc6d7d1f6fc30609ea0aa6", size = 381094, upload-time = "2025-10-06T14:09:36.268Z" }, + { url = "https://files.pythonhosted.org/packages/35/d8/611cc282502381ad855448643e1ad0538957fc82ae83dfe7762c14069e14/yarl-1.22.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b266bd01fedeffeeac01a79ae181719ff848a5a13ce10075adbefc8f1daee70e", size = 370944, upload-time = "2025-10-06T14:09:37.872Z" }, + { url = "https://files.pythonhosted.org/packages/2d/df/fadd00fb1c90e1a5a8bd731fa3d3de2e165e5a3666a095b04e31b04d9cb6/yarl-1.22.0-cp311-cp311-win32.whl", hash = "sha256:a9b1ba5610a4e20f655258d5a1fdc7ebe3d837bb0e45b581398b99eb98b1f5ca", size = 81804, upload-time = "2025-10-06T14:09:39.359Z" }, + { url = "https://files.pythonhosted.org/packages/b5/f7/149bb6f45f267cb5c074ac40c01c6b3ea6d8a620d34b337f6321928a1b4d/yarl-1.22.0-cp311-cp311-win_amd64.whl", hash = "sha256:078278b9b0b11568937d9509b589ee83ef98ed6d561dfe2020e24a9fd08eaa2b", size = 86858, upload-time = "2025-10-06T14:09:41.068Z" }, + { url = "https://files.pythonhosted.org/packages/2b/13/88b78b93ad3f2f0b78e13bfaaa24d11cbc746e93fe76d8c06bf139615646/yarl-1.22.0-cp311-cp311-win_arm64.whl", hash = "sha256:b6a6f620cfe13ccec221fa312139135166e47ae169f8253f72a0abc0dae94376", size = 81637, upload-time = "2025-10-06T14:09:42.712Z" }, + { url = "https://files.pythonhosted.org/packages/75/ff/46736024fee3429b80a165a732e38e5d5a238721e634ab41b040d49f8738/yarl-1.22.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e340382d1afa5d32b892b3ff062436d592ec3d692aeea3bef3a5cfe11bbf8c6f", size = 142000, upload-time = "2025-10-06T14:09:44.631Z" }, + { url = "https://files.pythonhosted.org/packages/5a/9a/b312ed670df903145598914770eb12de1bac44599549b3360acc96878df8/yarl-1.22.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f1e09112a2c31ffe8d80be1b0988fa6a18c5d5cad92a9ffbb1c04c91bfe52ad2", size = 94338, upload-time = "2025-10-06T14:09:46.372Z" }, + { url = "https://files.pythonhosted.org/packages/ba/f5/0601483296f09c3c65e303d60c070a5c19fcdbc72daa061e96170785bc7d/yarl-1.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:939fe60db294c786f6b7c2d2e121576628468f65453d86b0fe36cb52f987bd74", size = 94909, upload-time = "2025-10-06T14:09:48.648Z" }, + { url = "https://files.pythonhosted.org/packages/60/41/9a1fe0b73dbcefce72e46cf149b0e0a67612d60bfc90fb59c2b2efdfbd86/yarl-1.22.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1651bf8e0398574646744c1885a41198eba53dc8a9312b954073f845c90a8df", size = 372940, upload-time = "2025-10-06T14:09:50.089Z" }, + { url = "https://files.pythonhosted.org/packages/17/7a/795cb6dfee561961c30b800f0ed616b923a2ec6258b5def2a00bf8231334/yarl-1.22.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b8a0588521a26bf92a57a1705b77b8b59044cdceccac7151bd8d229e66b8dedb", size = 345825, upload-time = "2025-10-06T14:09:52.142Z" }, + { url = "https://files.pythonhosted.org/packages/d7/93/a58f4d596d2be2ae7bab1a5846c4d270b894958845753b2c606d666744d3/yarl-1.22.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:42188e6a615c1a75bcaa6e150c3fe8f3e8680471a6b10150c5f7e83f47cc34d2", size = 386705, upload-time = "2025-10-06T14:09:54.128Z" }, + { url = "https://files.pythonhosted.org/packages/61/92/682279d0e099d0e14d7fd2e176bd04f48de1484f56546a3e1313cd6c8e7c/yarl-1.22.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f6d2cb59377d99718913ad9a151030d6f83ef420a2b8f521d94609ecc106ee82", size = 396518, upload-time = "2025-10-06T14:09:55.762Z" }, + { url = "https://files.pythonhosted.org/packages/db/0f/0d52c98b8a885aeda831224b78f3be7ec2e1aa4a62091f9f9188c3c65b56/yarl-1.22.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50678a3b71c751d58d7908edc96d332af328839eea883bb554a43f539101277a", size = 377267, upload-time = "2025-10-06T14:09:57.958Z" }, + { url = "https://files.pythonhosted.org/packages/22/42/d2685e35908cbeaa6532c1fc73e89e7f2efb5d8a7df3959ea8e37177c5a3/yarl-1.22.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e8fbaa7cec507aa24ea27a01456e8dd4b6fab829059b69844bd348f2d467124", size = 365797, upload-time = "2025-10-06T14:09:59.527Z" }, + { url = "https://files.pythonhosted.org/packages/a2/83/cf8c7bcc6355631762f7d8bdab920ad09b82efa6b722999dfb05afa6cfac/yarl-1.22.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:433885ab5431bc3d3d4f2f9bd15bfa1614c522b0f1405d62c4f926ccd69d04fa", size = 365535, upload-time = "2025-10-06T14:10:01.139Z" }, + { url = "https://files.pythonhosted.org/packages/25/e1/5302ff9b28f0c59cac913b91fe3f16c59a033887e57ce9ca5d41a3a94737/yarl-1.22.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b790b39c7e9a4192dc2e201a282109ed2985a1ddbd5ac08dc56d0e121400a8f7", size = 382324, upload-time = "2025-10-06T14:10:02.756Z" }, + { url = "https://files.pythonhosted.org/packages/bf/cd/4617eb60f032f19ae3a688dc990d8f0d89ee0ea378b61cac81ede3e52fae/yarl-1.22.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31f0b53913220599446872d757257be5898019c85e7971599065bc55065dc99d", size = 383803, upload-time = "2025-10-06T14:10:04.552Z" }, + { url = "https://files.pythonhosted.org/packages/59/65/afc6e62bb506a319ea67b694551dab4a7e6fb7bf604e9bd9f3e11d575fec/yarl-1.22.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a49370e8f711daec68d09b821a34e1167792ee2d24d405cbc2387be4f158b520", size = 374220, upload-time = "2025-10-06T14:10:06.489Z" }, + { url = "https://files.pythonhosted.org/packages/e7/3d/68bf18d50dc674b942daec86a9ba922d3113d8399b0e52b9897530442da2/yarl-1.22.0-cp312-cp312-win32.whl", hash = "sha256:70dfd4f241c04bd9239d53b17f11e6ab672b9f1420364af63e8531198e3f5fe8", size = 81589, upload-time = "2025-10-06T14:10:09.254Z" }, + { url = "https://files.pythonhosted.org/packages/c8/9a/6ad1a9b37c2f72874f93e691b2e7ecb6137fb2b899983125db4204e47575/yarl-1.22.0-cp312-cp312-win_amd64.whl", hash = "sha256:8884d8b332a5e9b88e23f60bb166890009429391864c685e17bd73a9eda9105c", size = 87213, upload-time = "2025-10-06T14:10:11.369Z" }, + { url = "https://files.pythonhosted.org/packages/44/c5/c21b562d1680a77634d748e30c653c3ca918beb35555cff24986fff54598/yarl-1.22.0-cp312-cp312-win_arm64.whl", hash = "sha256:ea70f61a47f3cc93bdf8b2f368ed359ef02a01ca6393916bc8ff877427181e74", size = 81330, upload-time = "2025-10-06T14:10:13.112Z" }, + { 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" }, +]