From fd65157c25915111fee2b194d6e1d120e3ee147d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 13 Jun 2026 05:56:22 +0000 Subject: [PATCH 01/10] chore: update uv.lock --- uv.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uv.lock b/uv.lock index 1d5ca54..6579e02 100644 --- a/uv.lock +++ b/uv.lock @@ -2348,7 +2348,7 @@ wheels = [ [[package]] name = "winnow" -version = "0.2.12" +version = "0.2.13" source = { editable = "." } dependencies = [ { name = "croniter" }, From b786199a4f1eca94e471901cdae5f35b791fcb75 Mon Sep 17 00:00:00 2001 From: Holden Date: Sat, 13 Jun 2026 06:14:48 +0000 Subject: [PATCH 02/10] Add community scaffolding: CONTRIBUTING, SECURITY, issue templates, PR template - CONTRIBUTING.md: dev-branch workflow, uv setup, test/lint commands - SECURITY.md: private disclosure to holden@arch.fyi - .github/ISSUE_TEMPLATE/bug_report.yml: structured form with image tag, versions, logs - .github/ISSUE_TEMPLATE/feature_request.yml: problem/solution/alternatives form - .github/PULL_REQUEST_TEMPLATE.md: checklist enforcing dev branch + passing CI - pyproject.toml: add Changelog and Documentation URLs Co-Authored-By: Claude Sonnet 4.6 --- .github/ISSUE_TEMPLATE/bug_report.yml | 81 ++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.yml | 36 ++++++++++ .github/PULL_REQUEST_TEMPLATE.md | 26 +++++++ CONTRIBUTING.md | 41 +++++++++++ SECURITY.md | 17 +++++ pyproject.toml | 2 + 6 files changed, 203 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 CONTRIBUTING.md create mode 100644 SECURITY.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..538e7b3 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,81 @@ +name: Bug Report +description: Something isn't working as expected +title: "[Bug]: " +labels: ["bug"] +body: + - type: markdown + attributes: + value: | + Before filing, check the [Troubleshooting wiki](https://github.com/sudolulo/winnow/wiki/Troubleshooting) and [existing issues](https://github.com/sudolulo/winnow/issues). + + - type: dropdown + id: image-tag + attributes: + label: Image tag + description: Which winnow image are you running? + options: + - ":latest (NVIDIA CUDA)" + - ":rocm (AMD)" + - ":intel (Intel Arc / iGPU)" + - ":cpu (CPU only)" + - "Local install (uv)" + validations: + required: true + + - type: input + id: version + attributes: + label: winnow version + description: Output of `docker inspect ghcr.io/sudolulo/winnow: | grep org.opencontainers.image.version` or the version in `pyproject.toml`. + placeholder: "0.2.13" + validations: + required: true + + - type: input + id: immich-version + attributes: + label: Immich version + placeholder: "v1.110.0" + validations: + required: false + + - type: input + id: frigate-version + attributes: + label: Frigate version + placeholder: "0.16.0" + validations: + required: false + + - type: textarea + id: description + attributes: + label: What happened? + description: A clear description of the bug. + validations: + required: true + + - type: textarea + id: expected + attributes: + label: What did you expect to happen? + validations: + required: true + + - type: textarea + id: logs + attributes: + label: Relevant log output + description: Paste logs from `docker logs winnow` or `winnow.log`. Set `VERBOSE=true` for more detail. + render: text + validations: + required: false + + - type: textarea + id: compose + attributes: + label: Relevant compose / env config + description: Paste your `services.winnow` block. Redact your API key. + render: yaml + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..319e686 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,36 @@ +name: Feature Request +description: Suggest an improvement or new capability +title: "[Feature]: " +labels: ["enhancement"] +body: + - type: textarea + id: problem + attributes: + label: What problem does this solve? + description: Describe the use case or limitation you're running into. + validations: + required: true + + - type: textarea + id: solution + attributes: + label: Proposed solution + description: What would you like winnow to do? New env var, different behaviour, etc. + validations: + required: true + + - type: textarea + id: alternatives + attributes: + label: Alternatives considered + description: Any workarounds you've tried or other approaches you considered. + validations: + required: false + + - type: checkboxes + id: checklist + attributes: + label: Checklist + options: + - label: I checked existing issues and this hasn't been requested before. + required: true diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..ddf660e --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,26 @@ +## What does this PR do? + + + +## Why? + + + +Closes # + +## Changes + + + +- + +## Testing + + + +## Checklist + +- [ ] Targets the `dev` branch (not `main`) +- [ ] `uv run pytest` passes +- [ ] `uv run ruff check` passes +- [ ] `CHANGELOG.md` `[Unreleased]` section updated diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..06ff278 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,41 @@ +# Contributing to winnow + +Bug reports, feature requests, and pull requests are all welcome. + +## Before You Start + +- Check [existing issues](https://github.com/sudolulo/winnow/issues) to avoid duplicates. +- For large changes, open an issue first to discuss the approach. +- All PRs target the `dev` branch — never `main` directly. + +## Development Setup + +Requires Python 3.13+ and [uv](https://astral.sh/uv). + +```bash +git clone https://github.com/sudolulo/winnow.git +cd winnow +git checkout dev +uv sync +``` + +## Running Tests and Lint + +```bash +uv run pytest # run the test suite +uv run ruff check # lint +uv run ruff check --fix # auto-fix lint issues +``` + +CI runs both on every push and PR to `main` and `dev`. PRs must pass before merging. + +## Pull Request Guidelines + +- One logical change per PR. +- If you add behaviour, add a test for it. +- Keep the `CHANGELOG.md` entry in the `[Unreleased]` section updated. +- Commit messages should be plain English describing what changed and why. + +## License + +By submitting a contribution you agree that your work will be released under the project's [AGPLv3+ license](LICENSE). diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..b4b9b20 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,17 @@ +# Security Policy + +## Supported Versions + +Only the latest release is supported with security fixes. + +## Reporting a Vulnerability + +Please do **not** open a public GitHub issue for security vulnerabilities. + +Email **holden@arch.fyi** with: + +- A description of the vulnerability and its potential impact +- Steps to reproduce or a proof of concept +- Any suggested fix, if you have one + +You will receive an acknowledgement within 48 hours. If the vulnerability is confirmed, a fix will be released as soon as possible and you will be credited in the changelog unless you prefer otherwise. diff --git a/pyproject.toml b/pyproject.toml index e7dfe93..cf605c8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,6 +37,8 @@ winnow = "winnow.cli:main" [project.urls] Repository = "https://github.com/sudolulo/winnow" +Changelog = "https://github.com/sudolulo/winnow/blob/main/CHANGELOG.md" +Documentation = "https://github.com/sudolulo/winnow/wiki" [tool.uv] conflicts = [ From 7613fdcd5b6e373bb90e37224756a1baa0461d93 Mon Sep 17 00:00:00 2001 From: Holden Date: Sat, 13 Jun 2026 06:21:24 +0000 Subject: [PATCH 03/10] Add OCI image labels, issue template config, paths-ignore, and metadata fixes - Dockerfile: ARG VERSION + OCI labels (title, description, source, licenses, version) - release.yml: pass VERSION build-arg to all four image builds - docker-publish.yml: extend paths-ignore to cover community files - .github/ISSUE_TEMPLATE/config.yml: disable blank issues, link to Discussions and wiki - README.md: add Getting Help section - pyproject.toml: expand description; add System Administrators audience classifier Co-Authored-By: Claude Sonnet 4.6 --- .github/ISSUE_TEMPLATE/config.yml | 8 ++++++++ .github/workflows/docker-publish.yml | 4 ++++ .github/workflows/release.yml | 13 ++++++++++--- Dockerfile | 6 ++++++ README.md | 8 ++++++++ pyproject.toml | 3 ++- 6 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/config.yml diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..32acd81 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Question or help request + url: https://github.com/sudolulo/winnow/discussions + about: Ask questions and get help in GitHub Discussions + - name: Wiki / Documentation + url: https://github.com/sudolulo/winnow/wiki + about: Setup, troubleshooting, and FAQ diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 2f83e0e..eb743f5 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -6,6 +6,10 @@ on: paths-ignore: - "**.md" - "docs/**" + - "CONTRIBUTING.md" + - "SECURITY.md" + - ".github/ISSUE_TEMPLATE/**" + - ".github/PULL_REQUEST_TEMPLATE.md" - ".github/workflows/release.yml" - ".github/workflows/lint.yml" - ".github/dependabot.yml" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6e4c0b3..ae1308c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -125,6 +125,7 @@ jobs: file: ./Dockerfile platforms: linux/amd64,linux/arm64 push: true + build-args: VERSION=${{ steps.tag.outputs.VERSION }} cache-from: type=gha,scope=release-gpu cache-to: type=gha,mode=max,scope=release-gpu tags: | @@ -137,8 +138,10 @@ jobs: context: . file: ./Dockerfile platforms: linux/amd64,linux/arm64 - build-args: VARIANT=cpu push: true + build-args: | + VARIANT=cpu + VERSION=${{ steps.tag.outputs.VERSION }} cache-from: type=gha,scope=release-cpu cache-to: type=gha,mode=max,scope=release-cpu tags: | @@ -151,8 +154,10 @@ jobs: context: . file: ./Dockerfile platforms: linux/amd64 - build-args: VARIANT=rocm push: true + build-args: | + VARIANT=rocm + VERSION=${{ steps.tag.outputs.VERSION }} cache-from: type=gha,scope=release-rocm cache-to: type=gha,mode=max,scope=release-rocm tags: | @@ -165,8 +170,10 @@ jobs: context: . file: ./Dockerfile platforms: linux/amd64 - build-args: VARIANT=intel push: true + build-args: | + VARIANT=intel + VERSION=${{ steps.tag.outputs.VERSION }} cache-from: type=gha,scope=release-intel cache-to: type=gha,mode=max,scope=release-intel tags: | diff --git a/Dockerfile b/Dockerfile index 201586d..a621a9c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -65,6 +65,12 @@ RUN chmod +x /app/entrypoint.sh FROM base-${TARGETARCH}-${VARIANT} AS runtime ARG VARIANT=gpu +ARG VERSION=dev +LABEL org.opencontainers.image.title="winnow" \ + org.opencontainers.image.description="Selects diverse, high-quality photos from Immich as training data for Frigate face recognition and object classification." \ + org.opencontainers.image.source="https://github.com/sudolulo/winnow" \ + org.opencontainers.image.licenses="AGPL-3.0-or-later" \ + org.opencontainers.image.version="${VERSION}" ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get install -y --no-install-recommends \ diff --git a/README.md b/README.md index 2d5fd9b..f97035d 100644 --- a/README.md +++ b/README.md @@ -229,6 +229,14 @@ Requires Python 3.13+ and [uv](https://astral.sh/uv). An NVIDIA, AMD, or Intel G --- +## Getting Help + +- **[GitHub Discussions](https://github.com/sudolulo/winnow/discussions)** — questions, setup help, and general discussion +- **[Wiki](https://github.com/sudolulo/winnow/wiki)** — setup guide, troubleshooting, and FAQ +- **[Issues](https://github.com/sudolulo/winnow/issues)** — bugs and feature requests only + +--- + ## Attribution Based on [if_curator](https://github.com/ds-sebastian/if_curator) by Sebastian, licensed MIT. diff --git a/pyproject.toml b/pyproject.toml index cf605c8..920c9b6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "winnow" version = "0.2.13" -description = "Immich to Frigate training sets" +description = "Selects diverse, high-quality photos from Immich as training data for Frigate face recognition and object classification." license = "AGPL-3.0-or-later" requires-python = ">=3.13" authors = [{ name = "Holden Salomon", email = "holden@arch.fyi" }] @@ -9,6 +9,7 @@ keywords = ["immich", "frigate", "face-recognition", "training-data", "arcface", classifiers = [ "Development Status :: 3 - Alpha", "Intended Audience :: Developers", + "Intended Audience :: System Administrators", "License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)", "Programming Language :: Python :: 3.13", "Topic :: Scientific/Engineering :: Image Recognition", From cf760df930bca1c3cae985b1b4de850915c3c795 Mon Sep 17 00:00:00 2001 From: Holden Date: Sat, 13 Jun 2026 06:30:14 +0000 Subject: [PATCH 04/10] Fix build flow: lockfile bot, CPU multi-arch, paths-ignore, release guard - update-lockfile.yml: regenerate all four lockfiles (main + cpu/rocm/intel variants) on any pyproject change; add variant pyproject files to trigger - docker-publish.yml: add lockfiles to paths-ignore so the bot commit does not trigger a second Docker build; remove redundant CONTRIBUTING/SECURITY entries already covered by **.md; add QEMU to build-cpu; set CPU image to linux/amd64,linux/arm64 to match release - release.yml: inline lockfile generation now covers all variants; add workflow_dispatch guard that fails if not dispatched from main Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/docker-publish.yml | 13 +++++++++---- .github/workflows/release.yml | 21 +++++++++++++++++++-- .github/workflows/update-lockfile.yml | 25 ++++++++++++++++++------- 3 files changed, 46 insertions(+), 13 deletions(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index eb743f5..3bc2c26 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -6,13 +6,15 @@ on: paths-ignore: - "**.md" - "docs/**" - - "CONTRIBUTING.md" - - "SECURITY.md" - ".github/ISSUE_TEMPLATE/**" - ".github/PULL_REQUEST_TEMPLATE.md" - ".github/workflows/release.yml" - ".github/workflows/lint.yml" - ".github/dependabot.yml" + - "uv.lock" + - "uv-cpu.lock" + - "uv-rocm.lock" + - "uv-intel.lock" concurrency: group: docker-${{ github.ref }} @@ -143,7 +145,7 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} build-cpu: - name: Build CPU-only (amd64) + name: Build CPU (amd64 + arm64) runs-on: ubuntu-latest permissions: contents: read @@ -161,6 +163,9 @@ jobs: - name: Checkout repository uses: actions/checkout@v6 + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v4 @@ -185,7 +190,7 @@ jobs: with: context: . file: ./Dockerfile - platforms: linux/amd64 + platforms: linux/amd64,linux/arm64 build-args: VARIANT=cpu cache-from: type=gha,scope=linux/amd64-cpu cache-to: type=gha,mode=max,scope=linux/amd64-cpu diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ae1308c..a166c67 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -37,8 +37,25 @@ jobs: - name: Install uv uses: astral-sh/setup-uv@v7 - - name: Ensure uv.lock is current - run: uv lock + - name: Verify release branch + if: github.event_name == 'workflow_dispatch' + run: | + if [ "${{ github.ref_name }}" != "main" ]; then + echo "::error::Releases must be dispatched from main (current: ${{ github.ref_name }})" + exit 1 + fi + + - name: Ensure lockfiles are current + run: | + cp pyproject.toml _pyproject_orig.toml + for variant in cpu rocm intel; do + cp pyproject-${variant}.toml pyproject.toml + uv lock + cp uv.lock uv-${variant}.lock + done + cp _pyproject_orig.toml pyproject.toml + uv lock + rm _pyproject_orig.toml - name: Set up QEMU uses: docker/setup-qemu-action@v3 diff --git a/.github/workflows/update-lockfile.yml b/.github/workflows/update-lockfile.yml index 49895e0..ac48c06 100644 --- a/.github/workflows/update-lockfile.yml +++ b/.github/workflows/update-lockfile.yml @@ -5,6 +5,9 @@ on: push: paths: - 'pyproject.toml' + - 'pyproject-cpu.toml' + - 'pyproject-rocm.toml' + - 'pyproject-intel.toml' workflow_dispatch: jobs: @@ -30,24 +33,32 @@ jobs: - name: Set up Python run: uv python install 3.13 - - name: Regenerate lockfile - run: uv lock + - name: Regenerate all lockfiles + run: | + cp pyproject.toml _pyproject_orig.toml + for variant in cpu rocm intel; do + cp pyproject-${variant}.toml pyproject.toml + uv lock + cp uv.lock uv-${variant}.lock + done + cp _pyproject_orig.toml pyproject.toml + uv lock + rm _pyproject_orig.toml - name: Check for changes id: diff run: | - if git diff --quiet uv.lock; then + if git diff --quiet uv.lock uv-cpu.lock uv-rocm.lock uv-intel.lock; then echo "changed=false" >> "$GITHUB_OUTPUT" else echo "changed=true" >> "$GITHUB_OUTPUT" fi - - name: Commit and push updated lockfile + - name: Commit and push updated lockfiles if: steps.diff.outputs.changed == 'true' run: | git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add uv.lock - git commit -m "chore: update uv.lock" + git add uv.lock uv-cpu.lock uv-rocm.lock uv-intel.lock + git commit -m "chore: update lockfiles" git push - From 0e2915f564a14a8501423d4c0ac3be3603d84680 Mon Sep 17 00:00:00 2001 From: Holden Date: Sat, 13 Jun 2026 06:32:35 +0000 Subject: [PATCH 05/10] Polish build flow and README formatting - update-lockfile.yml: rename workflow to "Update lockfiles" - release.yml: add explicit uv python install 3.13 for consistency with other workflows - docker-publish.yml: rename cpu cache scope from linux/amd64-cpu to cpu (now multi-arch) - README: add GitHub release version badge and License badge - README: shorten QUALITY_REPLACEMENT table cell Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/docker-publish.yml | 4 ++-- .github/workflows/release.yml | 3 +++ .github/workflows/update-lockfile.yml | 2 +- README.md | 4 ++-- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 3bc2c26..dd592fe 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -192,8 +192,8 @@ jobs: file: ./Dockerfile platforms: linux/amd64,linux/arm64 build-args: VARIANT=cpu - cache-from: type=gha,scope=linux/amd64-cpu - cache-to: type=gha,mode=max,scope=linux/amd64-cpu + cache-from: type=gha,scope=cpu + cache-to: type=gha,mode=max,scope=cpu github-token: ${{ secrets.GITHUB_TOKEN }} push: true tags: ${{ steps.cpu-tag.outputs.tag }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a166c67..56e454f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -37,6 +37,9 @@ jobs: - name: Install uv uses: astral-sh/setup-uv@v7 + - name: Set up Python + run: uv python install 3.13 + - name: Verify release branch if: github.event_name == 'workflow_dispatch' run: | diff --git a/.github/workflows/update-lockfile.yml b/.github/workflows/update-lockfile.yml index ac48c06..b557522 100644 --- a/.github/workflows/update-lockfile.yml +++ b/.github/workflows/update-lockfile.yml @@ -1,5 +1,5 @@ # .github/workflows/update-lockfile.yml -name: Update uv.lock +name: Update lockfiles on: push: diff --git a/README.md b/README.md index f97035d..8a3d97f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # winnow -[![Docker](https://github.com/sudolulo/winnow/actions/workflows/docker-publish.yml/badge.svg)](https://github.com/sudolulo/winnow/actions/workflows/docker-publish.yml) [![Test](https://github.com/sudolulo/winnow/actions/workflows/test.yml/badge.svg)](https://github.com/sudolulo/winnow/actions/workflows/test.yml) [![Immich](https://img.shields.io/badge/Immich-v1.106%2B-blueviolet)](https://immich.app) [![Frigate](https://img.shields.io/badge/Frigate-Ready-brightgreen)](https://frigate.video) +[![Docker](https://github.com/sudolulo/winnow/actions/workflows/docker-publish.yml/badge.svg)](https://github.com/sudolulo/winnow/actions/workflows/docker-publish.yml) [![Test](https://github.com/sudolulo/winnow/actions/workflows/test.yml/badge.svg)](https://github.com/sudolulo/winnow/actions/workflows/test.yml) [![GitHub release](https://img.shields.io/github/v/release/sudolulo/winnow)](https://github.com/sudolulo/winnow/releases/latest) [![License: AGPL v3](https://img.shields.io/badge/License-AGPL_v3-blue.svg)](LICENSE) [![Immich](https://img.shields.io/badge/Immich-v1.106%2B-blueviolet)](https://immich.app) [![Frigate](https://img.shields.io/badge/Frigate-Ready-brightgreen)](https://frigate.video) **Docs:** [Setup](https://github.com/sudolulo/winnow/wiki/Setup) · [Troubleshooting](https://github.com/sudolulo/winnow/wiki/Troubleshooting) · [FAQ](https://github.com/sudolulo/winnow/wiki/FAQ) @@ -178,7 +178,7 @@ In scheduled mode the process (and loaded models) stays resident between runs. T | `MIN_CONFIDENCE` | `0.7` | Minimum Immich face detection confidence | | `BLUR_THRESHOLD` | `100.0` | Laplacian variance threshold — lower accepts more blur | | `MAX_AUTO_IMAGES` | `80` | Maximum training images per person in Frigate | -| `QUALITY_REPLACEMENT` | `true` | When at cap, replace the lowest-quality mapped training image if a better candidate is found. Only affects images winnow uploaded — manually added Frigate training files are never touched. Set `false` to disable and skip people already at cap | +| `QUALITY_REPLACEMENT` | `true` | When at cap, swap the lowest-quality uploaded image for a better candidate. Never touches manually added Frigate files. Set `false` to skip people already at cap | ### GPU & Models From ac249e6242144fd1ad40d397c0185c4e1ac7ba60 Mon Sep 17 00:00:00 2001 From: Holden Date: Sat, 13 Jun 2026 06:36:57 +0000 Subject: [PATCH 06/10] Update README to reflect current pipeline and features - Pipeline rewritten as 8 steps: separates thumbnail pass (quality filter + embeddings) from full-res download and crop; adds quality replacement logic at the upload step - Persistence note updated to cover rejected IDs and RETRY_REJECTED - Auto mode stopping threshold documented (20%/10% of median pairwise distance) - Add OUTPUT_DIR env var (was undocumented) - Local Install section notes interactive vs auto mode behaviour - QUALITY_REPLACEMENT description tightened Co-Authored-By: Claude Sonnet 4.6 --- README.md | 54 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 8a3d97f..5273dff 100644 --- a/README.md +++ b/README.md @@ -19,13 +19,11 @@ Immich library 1. Fetch all assets tagged with this person │ ▼ -2. Filter by recency (configurable years window) +2. Filter by recency (YEARS_FILTER) and skip already-uploaded + and rejected assets (persistent tracker in CACHE_DIR) │ ▼ -3. Skip already-uploaded assets (persistent tracker) - │ - ▼ -4. Quality filter — reject: +3. Quality filter — download preview thumbnails and reject: • Blurry images (Laplacian variance) • Grayscale / infrared (channel similarity) • Over- or underexposed @@ -33,25 +31,39 @@ Immich library • Face crops below minimum pixel size │ ▼ -5. Compute embeddings - • Faces → InsightFace (ArcFace / Buffalo_L) - • Objects → SigLIP (Vision Transformer) +4. Compute embeddings from the same preview thumbnails + • Faces → InsightFace (ArcFace / Buffalo_L) → 512-dim vector + • Objects → SigLIP (Vision Transformer) → 768-dim vector │ ▼ -6. Diversity selection +5. Diversity selection • K-Medoids clustering → one representative per natural group • Farthest Point Sampling → fill remaining slots with maximally spread picks • Hard example weighting — unusual angles and low-confidence detections are biased toward selection, since those are where models tend to fail - • Auto mode: stops when the next candidate is too similar to what's already chosen + • Auto mode: stops when similarity to the existing set exceeds a threshold + (20 % of median pairwise distance for faces, 10 % for objects) │ ▼ -7. Crop and deliver - • Face mode: aligned 112×112 crops uploaded directly to Frigate's face training API - • Object mode: YOLO-detected crops saved to disk +6. Download full-resolution originals from Immich + │ + ▼ +7. Crop and process + • Face mode: EXIF-corrected, landmark-aligned 112×112 crop (ArcFace format) + • Object mode: YOLOv9c detection → one crop per matched instance + │ + ▼ +8. Deliver + • Face mode: upload crops to Frigate's face registration API + ↳ below MAX_AUTO_IMAGES — upload freely + ↳ at cap + QUALITY_REPLACEMENT=true — swap the lowest-scoring tracked + image if the new candidate scores higher; manually added files are + never touched + ↳ at cap + QUALITY_REPLACEMENT=false — skip this person + • Object mode: save crops to disk → place into your Frigate data directory ``` -Uploaded asset IDs are persisted so the same image is never uploaded twice, even across runs weeks apart. +Uploaded and rejected asset IDs are persisted across runs. The same image is never processed twice; Frigate rejections are permanently skipped unless `RETRY_REJECTED=true`. --- @@ -59,7 +71,7 @@ Uploaded asset IDs are persisted so the same image is never uploaded twice, even **Face mode** (default) — extracts face crops using Immich's bounding box metadata, applies EXIF orientation correction, and aligns them to ArcFace's standard 112×112 format using 5-point facial landmarks. Crops are uploaded directly to Frigate's face registration API. -**Object mode** — runs each image through YOLOv9c to detect instances of a target class (dog, cat, car, etc.), crops each detection, and saves it to the output directory. Frigate has no API for uploading object training data; place the crops into your Frigate data directory manually. +**Object mode** — runs each full-resolution image through YOLOv9c to detect instances of a target class (dog, cat, car, etc.), crops each detection, and saves it to the output directory. Frigate has no API for uploading object training data; place the crops into your Frigate data directory manually. --- @@ -152,7 +164,7 @@ In scheduled mode the process (and loaded models) stays resident between runs. T | Variable | Default | Description | | :--- | :--- | :--- | | `TRAINING_MODE` | `face` | `face` — upload crops to Frigate; `object` — save crops to disk | -| `STRATEGY` | `auto` | `auto` (adaptive), `standard` (30 images), `broad` (100 images) | +| `STRATEGY` | `auto` | `auto` (embedding-based adaptive), `standard` (30 images), `broad` (100 images) | | `LIMIT` | *(unset)* | Exact image count — overrides `STRATEGY` | | `OBJECT_CLASS` | `dog` | Target class for object mode (any YOLO class: `dog`, `cat`, `car`, etc.) | | `AUTO_MODE` | *(auto)* | Force non-interactive mode in a terminal; auto-detected otherwise | @@ -178,7 +190,7 @@ In scheduled mode the process (and loaded models) stays resident between runs. T | `MIN_CONFIDENCE` | `0.7` | Minimum Immich face detection confidence | | `BLUR_THRESHOLD` | `100.0` | Laplacian variance threshold — lower accepts more blur | | `MAX_AUTO_IMAGES` | `80` | Maximum training images per person in Frigate | -| `QUALITY_REPLACEMENT` | `true` | When at cap, swap the lowest-quality uploaded image for a better candidate. Never touches manually added Frigate files. Set `false` to skip people already at cap | +| `QUALITY_REPLACEMENT` | `true` | When at cap, swap the lowest-scoring tracked image for a better candidate. Never touches manually added Frigate files. Set `false` to skip people already at cap | ### GPU & Models @@ -191,6 +203,12 @@ In scheduled mode the process (and loaded models) stays resident between runs. T | `HF_HOME` | *(system)* | HuggingFace model cache path (SigLIP) | | `INSIGHTFACE_HOME` | *(system)* | InsightFace model cache path (Buffalo_L) | +### Output + +| Variable | Default | Description | +| :--- | :--- | :--- | +| `OUTPUT_DIR` | `./frigate_train` | Directory for object-mode crops and the `winnow.log` file. In Docker, set this via the volume mount instead. | + ### Tracker Overrides *(one-shot — remove after use)* | Variable | Default | Description | @@ -218,6 +236,8 @@ uv run winnow Requires Python 3.13+ and [uv](https://astral.sh/uv). An NVIDIA, AMD, or Intel GPU is recommended — CPU mode works but embedding computation is slower. +When run with a terminal attached, winnow starts an interactive session: select which people to process and choose a strategy (auto, standard, broad, or a custom count) per person. Without a TTY — Docker, cron, or `AUTO_MODE=true` — it processes all people automatically using the configured defaults. + --- ## Requirements From 3da5f67c2128dd6c403cffa63ffb3ae9ed44291f Mon Sep 17 00:00:00 2001 From: Holden Date: Sat, 13 Jun 2026 06:51:21 +0000 Subject: [PATCH 07/10] Fix four quality-replacement bugs found by code re-audit C1/C4: Cap blur-score computation at 1440 px before calling assess_quality so scores are always on the same Laplacian scale as the embedding path (which operates on Immich preview thumbnails). Also converts the image to RGB before scoring and stores 0.0 on assess_quality failure so files uploaded without a score remain eligible for future quality replacement instead of occupying a slot permanently. C2: Fall back to the tracker's mapped-filename set as the pre-upload baseline when the Frigate GET /api/faces endpoint is unreachable at upload start. Previously, uploads that succeeded during a partial API outage were never mapped in frigate_files, leaving get_tracked_frigate_file_count permanently under-counting those files and allowing Frigate to exceed MAX_AUTO_IMAGES over time. C3: Track min_quality_score_for_slot when a quality-replacement delete succeeds but the subsequent upload fails. This ensures the freed slot can only be filled by a candidate that beats the deleted file's score, not just the next file in iteration order (which could be lower quality than what was deleted). Add get_tracked_frigate_filenames() to upload_tracker and expand tracker tests to cover the new function and exclude-parameter behaviour. Co-Authored-By: Claude Sonnet 4.6 --- tests/test_upload_tracker.py | 62 ++++++++++++++++++++++++++++++++++ winnow/executor.py | 64 ++++++++++++++++++++++++++++++++---- winnow/frigate_api.py | 5 +++ winnow/upload_tracker.py | 25 +++++++++++--- 4 files changed, 145 insertions(+), 11 deletions(-) diff --git a/tests/test_upload_tracker.py b/tests/test_upload_tracker.py index b5b6acd..fcdd37c 100644 --- a/tests/test_upload_tracker.py +++ b/tests/test_upload_tracker.py @@ -156,3 +156,65 @@ def test_get_lowest_quality_mapped_file_skips_unscored(): result = get_lowest_quality_mapped_file("Alice") assert result is not None assert result[1] == "asset-scored" # only scored file is a candidate + + +# ── get_tracked_frigate_filenames ───────────────────────────────────────────── + +def test_get_tracked_frigate_filenames_empty(): + from winnow.upload_tracker import get_tracked_frigate_filenames + assert get_tracked_frigate_filenames("Alice") == set() + + +def test_get_tracked_frigate_filenames_returns_mapped(): + from winnow.upload_tracker import get_tracked_frigate_filenames, record_frigate_file + record_frigate_file("Alice", "Alice-1000.webp", "asset-a") + record_frigate_file("Alice", "Alice-1001.webp", "asset-b") + assert get_tracked_frigate_filenames("Alice") == {"Alice-1000.webp", "Alice-1001.webp"} + + +def test_get_tracked_frigate_filenames_excludes_removed(): + from winnow.upload_tracker import ( + get_tracked_frigate_filenames, + record_frigate_file, + remove_frigate_file, + ) + record_frigate_file("Alice", "Alice-1000.webp", "asset-a") + record_frigate_file("Alice", "Alice-1001.webp", "asset-b") + remove_frigate_file("Alice", "Alice-1000.webp") + assert get_tracked_frigate_filenames("Alice") == {"Alice-1001.webp"} + + +def test_get_tracked_frigate_filenames_isolated_by_person(): + from winnow.upload_tracker import get_tracked_frigate_filenames, record_frigate_file + record_frigate_file("Alice", "Alice-1000.webp", "asset-a") + record_frigate_file("Bob", "Bob-2000.webp", "asset-b") + assert get_tracked_frigate_filenames("Alice") == {"Alice-1000.webp"} + assert get_tracked_frigate_filenames("Bob") == {"Bob-2000.webp"} + + +# ── get_lowest_quality_mapped_file with exclude ─────────────────────────────── + +def test_get_lowest_quality_exclude_skips_specified_file(): + from winnow.upload_tracker import ( + get_lowest_quality_mapped_file, + mark_uploaded, + record_frigate_file, + ) + mark_uploaded("asset-lo", person_name="Alice", score=0.10) + mark_uploaded("asset-hi", person_name="Alice", score=0.90) + record_frigate_file("Alice", "Alice-lo.webp", "asset-lo") + record_frigate_file("Alice", "Alice-hi.webp", "asset-hi") + result = get_lowest_quality_mapped_file("Alice", exclude={"Alice-lo.webp"}) + assert result is not None + assert result[1] == "asset-hi" # lo was excluded; hi is returned + + +def test_get_lowest_quality_exclude_all_returns_none(): + from winnow.upload_tracker import ( + get_lowest_quality_mapped_file, + mark_uploaded, + record_frigate_file, + ) + mark_uploaded("asset-a", person_name="Alice", score=0.50) + record_frigate_file("Alice", "Alice-a.webp", "asset-a") + assert get_lowest_quality_mapped_file("Alice", exclude={"Alice-a.webp"}) is None diff --git a/winnow/executor.py b/winnow/executor.py index fbf6e40..0289061 100644 --- a/winnow/executor.py +++ b/winnow/executor.py @@ -17,9 +17,11 @@ from .frigate_api import delete_frigate_person_files, get_frigate_person_files from .image_processing import process_face_mode, process_full_mode, process_object_mode from .immich_api import fetch_face_data, fetch_full_image from .log_config import console +from .quality import assess_quality from .upload_tracker import ( get_lowest_quality_mapped_file, get_tracked_frigate_file_count, + get_tracked_frigate_filenames, mark_rejected, mark_uploaded, record_frigate_file, @@ -205,7 +207,22 @@ def execute_jobs(jobs: list[dict]) -> None: # Record which asset produced which output file filename = f"{count}.jpg" asset_map[filename] = asset["id"] - score_map[filename] = asset.get("quality_score") or asset.get("face_confidence") + score_map[filename] = asset.get("quality_score") + # Time-spread path: compute blur score from the downloaded + # image. Cap at 1440px so the scale matches the preview + # thumbnails the embedding path uses for scoring — Laplacian + # variance grows with resolution, making full-res and + # thumbnail scores incomparable if left uncapped. + if mode == "face" and score_map[filename] is None: + try: + score_img = img.convert("RGB") if img.mode != "RGB" else img + if score_img.width > 1440 or score_img.height > 1440: + score_img = score_img.copy() + score_img.thumbnail((1440, 1440), Image.LANCZOS) + score_map[filename] = assess_quality(score_img).blur_score + except Exception as exc: + logger.debug(f"Quality score fallback for {asset['id']}: {exc}") + score_map[filename] = 0.0 # unknown quality — treat as lowest # Also record object-mode variant filenames if mode == "object": for f in sorted(os.listdir(person_dir)): @@ -320,14 +337,41 @@ def upload_to_frigate(jobs: list[dict]) -> None: # Snapshot live Frigate files for post-upload reconciliation diff only. # effective_count is sourced from the tracker (mapped files) so that # manually-added Frigate files don't consume winnow's managed quota. - known_frigate_files_at_start: set[str] = set(get_frigate_person_files(name) or []) + _snapshot = get_frigate_person_files(name) + if _snapshot is None: + # Frigate GET is down; fall back to the tracker's mapped filenames + # as the pre-upload baseline. reconciliation will still work unless + # there are concurrent manual uploads (handled by >target guard). + logger.warning( + f"{name}: Frigate API unreachable at upload start" + " — using tracker baseline for post-upload reconciliation" + ) + known_frigate_files_at_start: set[str] = get_tracked_frigate_filenames(name) + else: + known_frigate_files_at_start: set[str] = set(_snapshot) effective_count = get_tracked_frigate_file_count(name) quality_replacement = job.get("config", {}).get("quality_replacement", False) actually_uploaded: list[tuple[str, str | None]] = [] + failed_deletes: set[str] = set() + min_quality_score_for_slot: float | None = None for fname in person_files: fpath = os.path.join(person_dir, fname) + # If a previous replacement delete succeeded but that upload failed, + # require the next candidate to beat the deleted file's score so the + # freed slot isn't filled with something worse than what we removed. + if min_quality_score_for_slot is not None: + file_score = score_map.get(fname) + if file_score is None or file_score <= min_quality_score_for_slot: + score_str = f"{file_score:.3f}" if file_score is not None else "N/A" + progress.console.print( + f" [dim]⏭ {fname}: score {score_str} ≤ freed slot floor" + f" {min_quality_score_for_slot:.3f}, skipping[/dim]" + ) + progress.advance(upload_task) + continue + at_cap = effective_count >= Config.MAX_AUTO_IMAGES if at_cap: if not quality_replacement: @@ -339,7 +383,7 @@ def upload_to_frigate(jobs: list[dict]) -> None: progress.console.print(f" [dim]⏭ {fname}: no confidence score, skipping replacement[/dim]") progress.advance(upload_task) continue - worst = get_lowest_quality_mapped_file(name) + worst = get_lowest_quality_mapped_file(name, exclude=failed_deletes) if worst is None or new_score <= worst[2]: worst_score_str = f"{worst[2]:.3f}" if worst is not None else "N/A" progress.console.print( @@ -357,11 +401,10 @@ def upload_to_frigate(jobs: list[dict]) -> None: if delete_frigate_person_files(name, [worst_frigate_file]): remove_frigate_file(name, worst_frigate_file) effective_count -= 1 + min_quality_score_for_slot = worst_score else: logger.warning(f"Failed to delete {worst_frigate_file} for {name}, skipping replacement") - # Remove from tracker so the next candidate targets a different file. - # The file stays in Frigate (unmapped, like a manually-added file). - remove_frigate_file(name, worst_frigate_file) + failed_deletes.add(worst_frigate_file) progress.advance(upload_task) continue @@ -377,6 +420,7 @@ def upload_to_frigate(jobs: list[dict]) -> None: uploaded += 1 person_uploaded += 1 effective_count += 1 + min_quality_score_for_slot = None asset_id = asset_map.get(fname) if asset_id: @@ -438,7 +482,13 @@ def upload_to_frigate(jobs: list[dict]) -> None: progress.advance(upload_task) - # Batch-map Frigate filenames to asset IDs now that all uploads are done + if min_quality_score_for_slot is not None: + logger.warning( + f"{name}: freed replacement slot (floor {min_quality_score_for_slot:.3f})" + " was not filled this run — will be available next run" + ) + + # Batch-map Frigate filenames to asset IDs now that all uploads are done. if actually_uploaded: _reconcile_frigate_mappings(name, known_frigate_files_at_start, actually_uploaded) diff --git a/winnow/frigate_api.py b/winnow/frigate_api.py index c51115e..b6d4950 100644 --- a/winnow/frigate_api.py +++ b/winnow/frigate_api.py @@ -73,6 +73,11 @@ def delete_frigate_person_files(person_name: str, filenames: list[str]) -> bool: if resp.ok: logger.debug(f"Deleted {len(filenames)} Frigate file(s) for {person_name}") return True + if resp.status_code == 404: + # File already absent — stale tracker entry. Return True so the caller + # removes it from the tracker and frees the slot cleanly. + logger.warning(f"Frigate file(s) not found for {person_name} (stale tracker entry?): {filenames}") + return True logger.warning(f"Frigate delete returned {resp.status_code} for {person_name}") return False except Exception as e: diff --git a/winnow/upload_tracker.py b/winnow/upload_tracker.py index 3b6de12..aa450e9 100644 --- a/winnow/upload_tracker.py +++ b/winnow/upload_tracker.py @@ -12,7 +12,7 @@ Both are excluded from future candidate pools. To reset: by_person schema (frigate_uploaded_ids.json): { "asset_ids": ["immich-id-1", ...], # all assets we attempted to upload - "scores": {"immich-id-1": 0.953}, # Immich face confidence at upload time + "scores": {"immich-id-1": 450.3}, # Laplacian blur variance at upload time "frigate_files": {"PersonName-123.webp": "immich-id-1"}, # Frigate filename → asset ID "frigate_count": 42 # last known Frigate training image count } @@ -158,9 +158,26 @@ def get_tracked_frigate_file_count(person_name: str) -> int: return len(entry["frigate_files"]) -def get_lowest_quality_mapped_file(person_name: str) -> tuple[str, str, float] | None: +def get_tracked_frigate_filenames(person_name: str) -> set[str]: + """Return the set of Frigate filenames currently mapped in the tracker for a person. + + Used as a pre-upload baseline when the Frigate GET API is unreachable at + upload start, so reconciliation can still identify newly uploaded files. + """ + data = _load(UPLOAD_TRACKER_FILE) + entry = _migrate_entry(data.get("by_person", {}).get(person_name, {})) + return set(entry["frigate_files"].keys()) + + +def get_lowest_quality_mapped_file( + person_name: str, exclude: set[str] | None = None +) -> tuple[str, str, float] | None: """Return (frigate_filename, asset_id, score) for the mapped file with the lowest - confidence score, or None if no mapped files with known scores exist.""" + quality score, or None if no mapped files with known scores exist. + + Pass `exclude` to skip files that failed to delete this run without removing + them from the tracker — they remain candidates on the next run. + """ data = _load(UPLOAD_TRACKER_FILE) entry = _migrate_entry(data.get("by_person", {}).get(person_name, {})) frigate_files = entry.get("frigate_files", {}) @@ -168,7 +185,7 @@ def get_lowest_quality_mapped_file(person_name: str) -> tuple[str, str, float] | candidates = [ (frigate_filename, asset_id, scores[asset_id]) for frigate_filename, asset_id in frigate_files.items() - if asset_id in scores + if asset_id in scores and (exclude is None or frigate_filename not in exclude) ] if not candidates: return None From 2cee73fd6fbba69a5e1c259df7de10b2eeb9ddcb Mon Sep 17 00:00:00 2001 From: Holden Date: Sat, 13 Jun 2026 06:52:09 +0000 Subject: [PATCH 08/10] Bump version to 0.3.0 and update CHANGELOG Co-Authored-By: Claude Sonnet 4.6 --- CHANGELOG.md | 18 ++++++++++++++++++ pyproject.toml | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ec2b670..6237325 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.3.0] - 2026-06-13 + +### Added + +- **`get_tracked_frigate_filenames()`** — new upload-tracker function that returns the set of Frigate filenames currently mapped for a person. Used internally as a reconciliation baseline when the Frigate GET endpoint is unreachable; also available to callers that need the mapped filename set without a count. +- **Community scaffolding**: `CONTRIBUTING.md`, `SECURITY.md`, GitHub issue templates (bug report, feature request), and pull request template. +- **OCI image labels**: `org.opencontainers.image.*` labels added to the runtime stage of the Dockerfile so image metadata is surfaced by container registries. +- **Additional tracker tests**: coverage added for `get_tracked_frigate_filenames` and for `get_lowest_quality_mapped_file` with the `exclude` parameter. + +### Fixed + +- **Quality score scale mismatch (USE_FULL_RESOLUTION=true)**: the time-spread quality-score fallback called `assess_quality` on the full-resolution download, while the embedding path always scores on preview thumbnails. Laplacian variance scales with image resolution, so the two paths produced incomparable scores for people with mixed-mode files. The fallback now caps the image at 1440 px before scoring to match the thumbnail scale. +- **assess_quality failure left file permanently unreplaceable**: if `assess_quality` raised an exception (e.g. an RGBA image with an unsupported channel count), `score_map` kept `None` and `mark_uploaded(score=None)` skipped writing the score. The uploaded file was then permanently invisible to `get_lowest_quality_mapped_file` because it had no entry in `scores{}`. The fallback now converts the image to RGB before scoring and stores `0.0` on any exception, so every uploaded file is eligible for future quality replacement. +- **Uploads during Frigate GET outage never mapped**: when `GET /api/faces` failed at upload start, the reconciliation guard (`_snapshot is not None`) correctly skipped the post-upload diff — but uploads that succeeded during the outage were never recorded in `frigate_files`, causing `get_tracked_frigate_file_count` to permanently under-report and Frigate to eventually exceed `MAX_AUTO_IMAGES`. The code now uses the tracker's mapped filenames as a pre-upload baseline when the live snapshot is unavailable, so reconciliation proceeds normally (the `>target` guard handles concurrent external uploads as before). +- **Freed quality-replacement slot could be filled by a worse image**: when a replacement delete succeeded but the subsequent upload failed all retries, `effective_count` stayed decremented and the next file in the iteration uploaded unconditionally — it could have a lower quality score than the file that was deleted. A `min_quality_score_for_slot` variable now records the deleted file's score on a successful delete; any candidate that doesn't beat that floor is skipped until the slot is filled by a qualifying image or the run ends. +- **CI multi-arch and lockfile bot**: hardened the build workflow — lockfile-update bot no longer races against Docker publish on the same push event; CPU image now builds for both `linux/amd64` and `linux/arm64`; `paths-ignore` prevents documentation-only pushes from triggering image builds. +- **README pipeline diagram updated**: step 8 now explicitly documents the quality-replacement decision tree (`below cap → upload`, `at cap + enabled → swap if better`, `at cap + disabled → skip`). + ## [0.2.13] - 2026-06-13 ### Added diff --git a/pyproject.toml b/pyproject.toml index 920c9b6..1e48960 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "winnow" -version = "0.2.13" +version = "0.3.0" description = "Selects diverse, high-quality photos from Immich as training data for Frigate face recognition and object classification." license = "AGPL-3.0-or-later" requires-python = ">=3.13" From b29b98fa29baf39f49ff7fd2d4016e9b5f8f47cd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 13 Jun 2026 06:53:48 +0000 Subject: [PATCH 09/10] chore: update lockfiles --- uv-cpu.lock | 8 ++--- uv-intel.lock | 2 +- uv-rocm.lock | 92 +++++++++++++++++++++++++-------------------------- uv.lock | 66 ++++++++++++++++++------------------ 4 files changed, 84 insertions(+), 84 deletions(-) diff --git a/uv-cpu.lock b/uv-cpu.lock index 2e3bf55..a1dfbc5 100644 --- a/uv-cpu.lock +++ b/uv-cpu.lock @@ -1724,7 +1724,7 @@ wheels = [ [[package]] name = "transformers" -version = "5.11.0" +version = "5.12.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "huggingface-hub" }, @@ -1737,9 +1737,9 @@ dependencies = [ { name = "tqdm" }, { name = "typer" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7d/4a/2ee05f9a06bb2dd34cac951548553738a826762fb7adfa7ce256daeaeac8/transformers-5.11.0.tar.gz", hash = "sha256:1dbecade5b8a09bdf3e9e8fdfc9d312cb9eccf5a201080dc894b373f0f3eb5f4", size = 8874363, upload-time = "2026-06-10T16:31:52.441Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0c/f9/4552e2ba55db1c943aea0d4c09a32e9cbe5445b9eabe9856900de503dc8f/transformers-5.12.0.tar.gz", hash = "sha256:f0cf42ae1464c2eb41e7e0e66d7fd4b66145f48af17093b4cc0b2e9781faa7f4", size = 8923020, upload-time = "2026-06-12T14:39:20.43Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/40/30fdefda9b8aff8d36054dbb37d8e1464cc1130c29623403c3c2147bdee1/transformers-5.11.0-py3-none-any.whl", hash = "sha256:06d0a34eab529955f8e704b530663c7d6bd6f9c7a9e47a05cddb4a44e7b6566b", size = 11094597, upload-time = "2026-06-10T16:31:49.08Z" }, + { url = "https://files.pythonhosted.org/packages/ff/1f/d385913c38e900d23b728a4188fee625f2fccb306aeebc59be9d91404a5a/transformers-5.12.0-py3-none-any.whl", hash = "sha256:500be9eb644ede81c3103eee7687fc36d05dd75d1c76686c3820b26396fe7c7c", size = 11150246, upload-time = "2026-06-12T14:39:17.009Z" }, ] [[package]] @@ -1832,7 +1832,7 @@ wheels = [ [[package]] name = "winnow" -version = "0.2.9" +version = "0.2.13" source = { editable = "." } dependencies = [ { name = "croniter" }, diff --git a/uv-intel.lock b/uv-intel.lock index b8ac8b0..3758e0f 100644 --- a/uv-intel.lock +++ b/uv-intel.lock @@ -1889,7 +1889,7 @@ wheels = [ [[package]] name = "winnow" -version = "0.2.11" +version = "0.2.13" source = { editable = "." } dependencies = [ { name = "croniter" }, diff --git a/uv-rocm.lock b/uv-rocm.lock index 0c07ff3..57b8184 100644 --- a/uv-rocm.lock +++ b/uv-rocm.lock @@ -409,7 +409,7 @@ wheels = [ [[package]] name = "huggingface-hub" -version = "1.18.0" +version = "1.19.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, @@ -423,9 +423,9 @@ dependencies = [ { name = "typer" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fb/d8/748ea0a47f0fa15227fe682f7a80826b4b7c096e4818044b8f56d6cb66d6/huggingface_hub-1.18.0.tar.gz", hash = "sha256:f0c5ecd1ef8c6a60f86f61ee278f2c1570ba9e279c9f54de9094210723b3613b", size = 812699, upload-time = "2026-06-05T09:26:33.401Z" } +sdist = { url = "https://files.pythonhosted.org/packages/88/27/629cfe58c582f92ded066c4a07d1a057ff617118ab7973200f770bd853cb/huggingface_hub-1.19.0.tar.gz", hash = "sha256:fd771622182d40977272a923953ee3b1b13538f9f8a7f5d78398f10af0f1c0bd", size = 824721, upload-time = "2026-06-11T12:33:18.665Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/03/40a05316cb6616e5b7efd7773656441ab04b4b022c2199e79bb4622a92a3/huggingface_hub-1.18.0-py3-none-any.whl", hash = "sha256:729be4a976fb706dcc02d176bcda8a3f32bdf21a294e8f4b3dda6fbcbc9c1ab1", size = 684411, upload-time = "2026-06-05T09:26:31.48Z" }, + { url = "https://files.pythonhosted.org/packages/b2/a5/558da89f66464d8d0229ff497e8b8666977de2d8cf48c28a2862ecf1250f/huggingface_hub-1.19.0-py3-none-any.whl", hash = "sha256:1dc72e1f6b4d6df6b30eb72e57d00514ef453d660f04af2b87f0e67267f31ee0", size = 693398, upload-time = "2026-06-11T12:33:16.695Z" }, ] [[package]] @@ -617,7 +617,7 @@ wheels = [ [[package]] name = "matplotlib" -version = "3.10.9" +version = "3.11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "contourpy" }, @@ -630,36 +630,36 @@ dependencies = [ { name = "pyparsing" }, { name = "python-dateutil" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/63/1b/4be5be87d43d327a0cf4de1a56e86f7f84c89312452406cf122efe2839e6/matplotlib-3.10.9.tar.gz", hash = "sha256:fd66508e8c6877d98e586654b608a0456db8d7e8a546eb1e2600efd957302358", size = 34811233, upload-time = "2026-04-24T00:14:13.539Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1f/24/080c99d223d158d3a8902769269ab6da5b50f7a0e6e072513907e02b7a6c/matplotlib-3.11.0.tar.gz", hash = "sha256:68c0c7be01b30dcca3638934f7f591df73401235cbdbf0d1ab1c71e7db7f8b57", size = 33251176, upload-time = "2026-06-12T02:29:15.508Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/aa/d3/8d4f6afbecb49fc04e060a57c0fce39ea51cc163a6bd87303ccd698e4fa6/matplotlib-3.10.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b580440f1ff81a0e34122051a3dfabb7e4b7f9e380629929bde0eff9af72165f", size = 8320331, upload-time = "2026-04-24T00:12:39.688Z" }, - { url = "https://files.pythonhosted.org/packages/63/d9/9e14bc7564bf92d5ffa801ae5fac819ce74b925dfb55e3ebde61a3bbad3e/matplotlib-3.10.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b1b745c489cd1a77a0dc1120a05dc87af9798faebc913601feb8c73d89bf2d1e", size = 8216461, upload-time = "2026-04-24T00:12:42.494Z" }, - { url = "https://files.pythonhosted.org/packages/8a/17/4402d0d14ccf1dfc70932600b68097fbbf9c898a4871d2cbbe79c7801a32/matplotlib-3.10.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8f3bcac1ca5ed000a6f4337d47ba67dfddf37ed6a46c15fd7f014997f7bf865f", size = 8790091, upload-time = "2026-04-24T00:12:44.789Z" }, - { url = "https://files.pythonhosted.org/packages/3e/0b/322aeec06dd9b91411f92028b37d447342770a24392aa4813e317064dad5/matplotlib-3.10.9-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a8d66a55def891c33147ba3ba9bfcabf0b526a43764c818acbb4525e5ed0838", size = 9605027, upload-time = "2026-04-24T00:12:47.583Z" }, - { url = "https://files.pythonhosted.org/packages/74/88/5f13482f55e7b00bcfc09838b093c2456e1379978d2a146844aae05350ad/matplotlib-3.10.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d843374407c4017a6403b59c6c81606773d136f3259d5b6da3131bc814542cc2", size = 9671269, upload-time = "2026-04-24T00:12:50.878Z" }, - { url = "https://files.pythonhosted.org/packages/c5/e0/0840fd2f93da988ec660b8ad1984abe9f25d2aed22a5e394ff1c68c88307/matplotlib-3.10.9-cp313-cp313-win_amd64.whl", hash = "sha256:f4399f64b3e94cd500195490972ae1ee81170df1636fa15364d157d5bdd7b921", size = 8217588, upload-time = "2026-04-24T00:12:53.784Z" }, - { url = "https://files.pythonhosted.org/packages/47/b9/d706d06dd605c49b9f83a2aed8c13e3e5db70697d7a80b7e3d7915de6b17/matplotlib-3.10.9-cp313-cp313-win_arm64.whl", hash = "sha256:ba7b3b8ef09eab7df0e86e9ae086faa433efbfbdb46afcb3aa16aabf779469a8", size = 8136913, upload-time = "2026-04-24T00:12:56.501Z" }, - { url = "https://files.pythonhosted.org/packages/9b/45/6e32d96978264c8ca8c4b1010adb955a1a49cfaf314e212bbc8908f04a61/matplotlib-3.10.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:09218df8a93712bd6ea133e83a153c755448cf7868316c531cffcc43f69d1cc9", size = 8368019, upload-time = "2026-04-24T00:12:58.896Z" }, - { url = "https://files.pythonhosted.org/packages/86/0a/c8e3d3bba245f0f7fc424937f8ff7ef77291a36af3edb97ccd78aa93d84f/matplotlib-3.10.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:82368699727bfb7b0182e1aa13082e3c08e092fa1a25d3e1fd92405bff96f6d4", size = 8264645, upload-time = "2026-04-24T00:13:01.406Z" }, - { url = "https://files.pythonhosted.org/packages/3d/aa/5bf5a14fe4fed73a4209a155606f8096ff797aad89c6c35179026571133e/matplotlib-3.10.9-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3225f4e1edcb8c86c884ddf79ebe20ecd0a67d30188f279897554ccd8fded4dc", size = 8802194, upload-time = "2026-04-24T00:13:03.702Z" }, - { url = "https://files.pythonhosted.org/packages/dd/5e/b4be852d6bba6fd15893fadf91ff26ae49cb91aac789e95dde9d342e664f/matplotlib-3.10.9-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:de2445a0c6690d21b7eb6ce071cebad6d40a2e9bdf10d039074a96ba19797b99", size = 9622684, upload-time = "2026-04-24T00:13:06.647Z" }, - { url = "https://files.pythonhosted.org/packages/4c/3d/ed428c971139112ef730f62770654d609467346d09d4b62617e1afd68a5a/matplotlib-3.10.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:b2b9516251cb89ff618d757daec0e2ed1bf21248013844a853d87ef85ab3081d", size = 9680790, upload-time = "2026-04-24T00:13:10.009Z" }, - { url = "https://files.pythonhosted.org/packages/e7/09/052e884aaf2b985c63cb79f715f1d5b6a3eaa7de78f6a52b9dbc077d5b53/matplotlib-3.10.9-cp313-cp313t-win_amd64.whl", hash = "sha256:e9fae004b941b23ff2edcf1567a857ed77bafc8086ffa258190462328434faf8", size = 8287571, upload-time = "2026-04-24T00:13:13.087Z" }, - { url = "https://files.pythonhosted.org/packages/f4/38/ae27288e788c35a4250491422f3db7750366fc8c97d6f36fbdecfc1f5518/matplotlib-3.10.9-cp313-cp313t-win_arm64.whl", hash = "sha256:6b63d9c7c769b88ab81e10dc86e4e0607cf56817b9f9e6cf24b2a5f1693b8e38", size = 8188292, upload-time = "2026-04-24T00:13:15.546Z" }, - { url = "https://files.pythonhosted.org/packages/d6/e6/3bd8afd04949f02eabc1c17115ea5255e19cacd4d06fc5abdde4eeb0052c/matplotlib-3.10.9-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:172db52c9e683f5d12eaf57f0f54834190e12581fe1cc2a19595a8f5acb4e77d", size = 8321276, upload-time = "2026-04-24T00:13:18.318Z" }, - { url = "https://files.pythonhosted.org/packages/41/86/86231232fff41c9f8e4a1a7d7a597d349a02527109c3af7d618366122139/matplotlib-3.10.9-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:97e35e8d39ccc85859095e01a53847432ba9a53ddf7986f7a54a11b73d0e143f", size = 8218218, upload-time = "2026-04-24T00:13:20.974Z" }, - { url = "https://files.pythonhosted.org/packages/85/8f/becc9722cafc64f5d2eb0b7c1bf5f585271c618a45dbd8fabeb021f898b6/matplotlib-3.10.9-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aba1615dabe83188e19d4f75a253c6a08423e04c1425e64039f800050a69de6b", size = 9608145, upload-time = "2026-04-24T00:13:23.228Z" }, - { url = "https://files.pythonhosted.org/packages/32/5d/f7e914f7d9325abff4057cee62c0fa70263683189f774473cbfb534cd13b/matplotlib-3.10.9-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34cf8167e023ad956c15f36302911d5406bd99a9862c1a8499ea6f7c0e015dc2", size = 9885085, upload-time = "2026-04-24T00:13:25.849Z" }, - { url = "https://files.pythonhosted.org/packages/a5/fd/fa69f2221534e80cc5772ac2b7d222011a2acafc2ec7216d5dd174c864ae/matplotlib-3.10.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:59476c6d29d612b8e9bb6ce8c5b631be6ba8f9e3a2421f22a02b192c7dd28716", size = 9672358, upload-time = "2026-04-24T00:13:28.906Z" }, - { url = "https://files.pythonhosted.org/packages/ab/1a/5a4f747a8b271cbb024946d2dd3c913ab5032ba430626f8c3528ada96b4b/matplotlib-3.10.9-cp314-cp314-win_amd64.whl", hash = "sha256:336b9acc64d309063126edcdaca00db9373af3c476bb94388fe9c5a53ad13e6f", size = 8349970, upload-time = "2026-04-24T00:13:31.904Z" }, - { url = "https://files.pythonhosted.org/packages/64/dc/95d60ecaefe30680a154b52ea96ab4b0dab547f1fd6aa12f5fb655e89cae/matplotlib-3.10.9-cp314-cp314-win_arm64.whl", hash = "sha256:2dc9477819ffd78ad12a20df1d9d6a6bd4fec6aaa9072681465fddca052f1456", size = 8272785, upload-time = "2026-04-24T00:13:34.511Z" }, - { url = "https://files.pythonhosted.org/packages/70/a0/005d68bc8b8418300ce6591f18586910a8526806e2ab663933d9f20a41e9/matplotlib-3.10.9-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:da4e09638420548f31c354032a6250e473c68e5a4e96899b4844cf39ddea23fe", size = 8367999, upload-time = "2026-04-24T00:13:36.962Z" }, - { url = "https://files.pythonhosted.org/packages/22/05/1236cc9290be70b2498af20ca348add76e3fffe7f67b477db5133a84f3ea/matplotlib-3.10.9-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:345f6f68ecc8da0ca56fad2ea08fde1a115eda530079eca185d50a7bc3e146c6", size = 8264543, upload-time = "2026-04-24T00:13:39.851Z" }, - { url = "https://files.pythonhosted.org/packages/cd/c2/071f5a5ff6c5bd63aaaf2f45c811d9bf2ced94bde188d9e1a519e21d0cba/matplotlib-3.10.9-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4edcfbd8565339aa62f1cd4012f7180926fdbe71850f7b0d3c379c175cd6b66c", size = 9622800, upload-time = "2026-04-24T00:13:42.296Z" }, - { url = "https://files.pythonhosted.org/packages/95/57/da7d1f10a85624b9e7db68e069dd94e58dc41dbf9463c5921632ecbe3661/matplotlib-3.10.9-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6be157fe17fc37cb95ac1d7374cf717ce9259616edec911a78d9d26dae8522d4", size = 9888561, upload-time = "2026-04-24T00:13:45.026Z" }, - { url = "https://files.pythonhosted.org/packages/67/b2/ef8d6bb59b0edb6c16c968b70f548aa13b54348972def5aa6ac85df67145/matplotlib-3.10.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:4e42042d54db34fda4e95a7bd3e5789c2a995d2dad3eb8850232ee534092fbbf", size = 9680884, upload-time = "2026-04-24T00:13:48.066Z" }, - { url = "https://files.pythonhosted.org/packages/61/1c/d21bfeb9931881ebe96bcfcff27c7ae4b160ae0ec291a714c42641a56d75/matplotlib-3.10.9-cp314-cp314t-win_amd64.whl", hash = "sha256:c27df8b3848f32a83d1767566595e43cfaa4460380974da06f4279a7ec143c39", size = 8432333, upload-time = "2026-04-24T00:13:51.008Z" }, - { url = "https://files.pythonhosted.org/packages/78/23/92493c3e6e1b635ccfff146f7b99e674808787915420373ac399283764c2/matplotlib-3.10.9-cp314-cp314t-win_arm64.whl", hash = "sha256:a49f1eadc84ca85fd72fa4e89e70e61bf86452df6f971af04b12c60761a0772c", size = 8324785, upload-time = "2026-04-24T00:13:53.633Z" }, + { url = "https://files.pythonhosted.org/packages/55/41/aa47f156b061d14c98b906f76c428507397708ec63ff94f410ae1752b426/matplotlib-3.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6ce3b839b34ae1f430b4616893a2945a2999debaa7e94e7e29a2a8bbf286f7b5", size = 9450532, upload-time = "2026-06-12T02:28:06.769Z" }, + { url = "https://files.pythonhosted.org/packages/8c/4f/5a9eb0375e81413953febf8af7b012a6b6357f53438a15c4f5ad86c6bbb5/matplotlib-3.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:373db8f91214e8ccaf35ac833cc1dd59dd961e148bbd55dd027141591dde1313", size = 9279760, upload-time = "2026-06-12T02:28:09.152Z" }, + { url = "https://files.pythonhosted.org/packages/a4/c0/1117d53077e3ac3152503a84e9cf7a5c239576805ee71276e80c2aaa7471/matplotlib-3.11.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:be152b7570324dc8d01574cc9474dd2d803237acf528bcbb5b211fa347461a09", size = 10031623, upload-time = "2026-06-12T02:28:11.26Z" }, + { url = "https://files.pythonhosted.org/packages/92/7e/e937138daffad65b71bf831a377809dcbc830fb4f31a31e067dc1faa2575/matplotlib-3.11.0-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:126f256df600652d7e4b394cf3164ff75210a00038f287c95a012a6f58d0e83f", size = 10839372, upload-time = "2026-06-12T02:28:14.102Z" }, + { url = "https://files.pythonhosted.org/packages/1d/c2/438ecc197ffb8023b6b9922915542f2172f5fd45b76703b0b4fc47322243/matplotlib-3.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:03acfeddf87b0dddb11b081ef7740ad445a3ca8bcb6b8e3011b08f2cf802b75c", size = 10924099, upload-time = "2026-06-12T02:28:16.383Z" }, + { url = "https://files.pythonhosted.org/packages/40/2e/395883da416f378b3ed2c9f3e843ac477eae1ce731b671b79adaa6f0bacd/matplotlib-3.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:ab3722f04f3ff34c23b5012c5873d2894174e06c3822fcdac3610965a5ac7d06", size = 9329727, upload-time = "2026-06-12T02:28:18.581Z" }, + { url = "https://files.pythonhosted.org/packages/61/82/2c388956abf8bf392dfb5b8917c502f1082df6a941b781ab8c8e5ba2474b/matplotlib-3.11.0-cp313-cp313-win_arm64.whl", hash = "sha256:c945824670fb8915b4ac879e5e61f3c58e0913022f70a0de4c082b17372f8771", size = 9003506, upload-time = "2026-06-12T02:28:20.474Z" }, + { url = "https://files.pythonhosted.org/packages/c8/c1/34454baa44da7975ada82e9aea37105ec47059514dc967d3be14426ba8dc/matplotlib-3.11.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3489c3dc487669b4a980bc3068f87856de7a1564248d3f6c629efb2a58b03f24", size = 9499838, upload-time = "2026-06-12T02:28:22.713Z" }, + { url = "https://files.pythonhosted.org/packages/b1/c3/98fe79a398cf232219f090163a7fa7e6766e9f2e0ad26df54d6f8934d8ee/matplotlib-3.11.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:6a98f5476ce784a50ce09998f4ae1e6a9f25043cef8a480c98949902eda74620", size = 9332298, upload-time = "2026-06-12T02:28:24.796Z" }, + { url = "https://files.pythonhosted.org/packages/95/e4/b4b7c33151e74e5c802f3cde1ba807ebfc38401e329b44e215a5888dd76d/matplotlib-3.11.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:565af866fd63e4bd3f987d580afe27c44c2552a3b3305f4ecbb85133601ea6f3", size = 10045491, upload-time = "2026-06-12T02:28:27.141Z" }, + { url = "https://files.pythonhosted.org/packages/71/28/394548efd68354110c1a1be11fe6b6e559e06d1a23da35908a0e316c55a9/matplotlib-3.11.0-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e6b3e64dea5062c570f04358e2711859f3531b459f29516274fbad889079e4f3", size = 10857059, upload-time = "2026-06-12T02:28:29.222Z" }, + { url = "https://files.pythonhosted.org/packages/c8/44/e7922e6e2a4d63bdfbc9dc4a53e3850ab438d46cf42e6779bb15ec92c948/matplotlib-3.11.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:942b37c5db1899610bd1543ce8e13e4ecff9a4633e7f63bb6aa9205d2644ebd1", size = 10939576, upload-time = "2026-06-12T02:28:31.66Z" }, + { url = "https://files.pythonhosted.org/packages/3d/be/b1ca96003a441d619b727fee21d671fdff7a5ce2f1bb797b2521aa2f679a/matplotlib-3.11.0-cp313-cp313t-win_amd64.whl", hash = "sha256:c08e649a6313e1291e713623b97a38e5bb4aa580b2a100a94a3309bc6b9c8eb3", size = 9379519, upload-time = "2026-06-12T02:28:33.888Z" }, + { url = "https://files.pythonhosted.org/packages/e3/72/4bf3b91821c34596dd6a7bdac5836d94f744144c8208939ef49d8ec43f7e/matplotlib-3.11.0-cp313-cp313t-win_arm64.whl", hash = "sha256:2746cd2c113742ff6ce37a864c5ac5fd7aa644568f445e66166e457ac78e40e0", size = 9055456, upload-time = "2026-06-12T02:28:35.878Z" }, + { url = "https://files.pythonhosted.org/packages/57/52/a94102ac99eb78e2fe9b826674f9ef9ee23327110ea6ab4776c1b4eb6209/matplotlib-3.11.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:3338e3e3de128cf50d0d2fb92a122815daf9c755bd882a474343c05f8fd7ec79", size = 9452137, upload-time = "2026-06-12T02:28:37.93Z" }, + { url = "https://files.pythonhosted.org/packages/7c/03/b8cdb625a21f710dfa11bbca1f48fb4057d2c0286975f8b415bf80942c99/matplotlib-3.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:25c2e5455efd8d99f41fb79871a31feb7d301569642e332ec58d72cfe9282bc3", size = 9281514, upload-time = "2026-06-12T02:28:40.028Z" }, + { url = "https://files.pythonhosted.org/packages/b7/2d/4e1240ea82ee197dfb3851e71f71c87eeeb975f1753b56a0588e4e80739a/matplotlib-3.11.0-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d9695457a467ff86d23f35037a43deb6f1134dd6d3e2ac8ce1e2087cff09ffb9", size = 10843005, upload-time = "2026-06-12T02:28:42.39Z" }, + { url = "https://files.pythonhosted.org/packages/29/dc/6377ecfaa5fef79430f74a1a16638b4e2aa30d4692bae2c19f9d76fe3b01/matplotlib-3.11.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:19c16c61dea63b3582918503e6b294193961261d9daa806d4ae2151f1ad05430", size = 11127459, upload-time = "2026-06-12T02:28:44.483Z" }, + { url = "https://files.pythonhosted.org/packages/6f/41/795c405aa7560443a3b01309424cde4a1113b85c90b8a63417444a749617/matplotlib-3.11.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2d72ea8b7924f3cb955e61518d21e43b3df1e6c8a793b480a0c1214f185d30ba", size = 10925160, upload-time = "2026-06-12T02:28:46.564Z" }, + { url = "https://files.pythonhosted.org/packages/1a/f7/3a9e6389a7cfaeff76c56e40c2dabcb13110e21e82f837228c834ebe748c/matplotlib-3.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:1c02da0a629dfa9debf52725ea06866b74c1fb70a895bae05e4493d34074f9f2", size = 9485186, upload-time = "2026-06-12T02:28:49.344Z" }, + { url = "https://files.pythonhosted.org/packages/8b/c0/396478ee7cf2091d182db8b4a8695f6a37f1ddb978989cf9dbb84cd5c123/matplotlib-3.11.0-cp314-cp314-win_arm64.whl", hash = "sha256:aa55d73b3117d4b07f959cd9eb6f69b375d8df3414139c479388e551aa5d999d", size = 9160349, upload-time = "2026-06-12T02:28:51.382Z" }, + { url = "https://files.pythonhosted.org/packages/c5/6f/1c3bd51bb2b34eaacdcf3c3d859dbb357f952fc8020c617dc118ad7c9e38/matplotlib-3.11.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:a9d8c6e7cd2f0ddf11d8d92e520dd1d9d2abb0cf6ac8831e338666c81e905847", size = 9500921, upload-time = "2026-06-12T02:28:53.443Z" }, + { url = "https://files.pythonhosted.org/packages/e0/0d/4d861d0121840cb1a3fd4a10deb211efd6fccd481ed23e553f31f4f4da4a/matplotlib-3.11.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:be050fcf32f729eda99f7f75a80bf67612ce16ab9ac1c23a387dcaede95cb70e", size = 9332190, upload-time = "2026-06-12T02:28:55.623Z" }, + { url = "https://files.pythonhosted.org/packages/4b/cb/22f6bc35711a0b5639a784e74e653e77c86210bd4304449dd399a482f74e/matplotlib-3.11.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dfabef0230d0697aa0d717385194dd41162e00207a68bf4abf94c2bf4c27dca0", size = 10854181, upload-time = "2026-06-12T02:28:57.856Z" }, + { url = "https://files.pythonhosted.org/packages/3f/7e/9a9eaca731a2939589da520f0ebe8fd8753d0f51fca98c7d20af6dbe261a/matplotlib-3.11.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1644db30e759199443493ac5e5caec24fdb775a8f6123021f85ba47c4133c3cb", size = 11137715, upload-time = "2026-06-12T02:29:00.555Z" }, + { url = "https://files.pythonhosted.org/packages/ef/f9/9b030b6088354acb0296871bb624b25befc1c42509d3c6cd17420c83a5b8/matplotlib-3.11.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:15b0d160079cb10699a0e98b5989c70677b2df7cacdc62af67c30f2facec46d9", size = 10939427, upload-time = "2026-06-12T02:29:02.527Z" }, + { url = "https://files.pythonhosted.org/packages/59/94/6b273eaee4ee250863567d100865da61a5c1527fa67f527b7ed22e0dd29c/matplotlib-3.11.0-cp314-cp314t-win_amd64.whl", hash = "sha256:446307e6b04b57b1f1239e228a1ec2af0d589a1008cebc3dfa3f5441d095cfb6", size = 9535809, upload-time = "2026-06-12T02:29:04.994Z" }, + { url = "https://files.pythonhosted.org/packages/60/95/1d36bddf2b7e2692c1540e78a6e5bc88bc1496b137e3e35a611f91b65ac3/matplotlib-3.11.0-cp314-cp314t-win_arm64.whl", hash = "sha256:652fb5696271d4c50f196d22a5ff4f8e4444c74f847423570d7dc0aa2bbd0159", size = 9209226, upload-time = "2026-06-12T02:29:07.033Z" }, ] [[package]] @@ -1158,17 +1158,17 @@ wheels = [ [[package]] name = "protobuf" -version = "7.35.0" +version = "7.35.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/60/fd/5b1491d9e4b586d621c54f4c36b888714164b6875f8d6afa3f9072906a51/protobuf-7.35.0.tar.gz", hash = "sha256:a2efd84605f41e559f1881b0912b44099d0a2ac9bf46b3474823f10fb393b0e6", size = 458677, upload-time = "2026-05-19T23:02:29.197Z" } +sdist = { url = "https://files.pythonhosted.org/packages/da/01/9ef0afd7999eb9badb3a768b4aedd78c86d4c65cfaf1958ab276199e76b4/protobuf-7.35.1.tar.gz", hash = "sha256:ce115a26fe0c39a2c29973d914d327e516a6455464489fe3cd1e51a1b354f81a", size = 458717, upload-time = "2026-06-11T21:55:40.257Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/83/ee/93d06e358a4aa32280b00e722d3ea0a1f25fc3cc5778d80581c9cca2c10e/protobuf-7.35.0-cp310-abi3-macosx_10_9_universal2.whl", hash = "sha256:66be6c513931c794fa92c080ffee41671390da3d79da219cf9c0c0907f035dda", size = 433225, upload-time = "2026-05-19T23:02:19.884Z" }, - { url = "https://files.pythonhosted.org/packages/8b/39/1c76c2da93f3c507e958e0aecee2391cc44d4625de6c728bbc555195b5a8/protobuf-7.35.0-cp310-abi3-manylinux2014_aarch64.whl", hash = "sha256:fcbe42a4ac09d3ec9c987ddfcd956afd0b15f1ff613bd8371bde9405ffd5c8e5", size = 328847, upload-time = "2026-05-19T23:02:22.3Z" }, - { url = "https://files.pythonhosted.org/packages/91/1a/39f7ce90a238c1a987a4d81ec26379e02ca0aff367de68e4a1fa474215b9/protobuf-7.35.0-cp310-abi3-manylinux2014_s390x.whl", hash = "sha256:4cbf5cc286130e06a6c9bbefac442431173906dfcc979712183d4adcc01b37ee", size = 344030, upload-time = "2026-05-19T23:02:23.591Z" }, - { url = "https://files.pythonhosted.org/packages/70/5b/6baf9008817964454055ff3fe65f1de0b5f1e26c80c82f7fb108b7cd4ea3/protobuf-7.35.0-cp310-abi3-manylinux2014_x86_64.whl", hash = "sha256:6c0f98f10c8a05ea30f8993dfef2de093d27b490fdae78bb60c8343795d55011", size = 327130, upload-time = "2026-05-19T23:02:24.637Z" }, - { url = "https://files.pythonhosted.org/packages/8e/e5/e46adb0badc388bfb84877a5f9f026aff63f60e611016cf64dbe77e05446/protobuf-7.35.0-cp310-abi3-win32.whl", hash = "sha256:4c4617b83ade0e279d1d2bfe04025a1adb87f9ed657de038620dc0ff959357f6", size = 428946, upload-time = "2026-05-19T23:02:25.741Z" }, - { url = "https://files.pythonhosted.org/packages/a7/ab/547fbd9e16d879dd13c167478f8ae0a83a428008ca07a5e06acdc23ad473/protobuf-7.35.0-cp310-abi3-win_amd64.whl", hash = "sha256:f05bcadf9a2a6b8dda047007075135fb7d08c73d9177aabc067e1be46881a201", size = 439996, upload-time = "2026-05-19T23:02:26.808Z" }, - { url = "https://files.pythonhosted.org/packages/b8/ef/50433d346c56657a70d27f156c7b349ac59a068b01de4eb796e747eecc43/protobuf-7.35.0-py3-none-any.whl", hash = "sha256:c13f325cf242bad135c350629eeb5d54b24228eb472fb3e2e9ebbd4c5dc20ca0", size = 171659, upload-time = "2026-05-19T23:02:27.842Z" }, + { url = "https://files.pythonhosted.org/packages/10/03/8aeeb7458d22546bf64b5250ca1daeb5ff757d900e8e4a7476c6f0db843e/protobuf-7.35.1-cp310-abi3-macosx_10_9_universal2.whl", hash = "sha256:24f857477359a85c0c235261b8ba905fd51b2562f4a64ca1df5473f29850cbf6", size = 433226, upload-time = "2026-06-11T21:55:31.719Z" }, + { url = "https://files.pythonhosted.org/packages/37/4b/dfb89eb0e652a1ff073c39a59fb5e3a83cfe9b57a2c83fa6d78270101767/protobuf-7.35.1-cp310-abi3-manylinux2014_aarch64.whl", hash = "sha256:11d6b0ec246892d85215b0a13ca6e0233cf5284b68f0ac02646427f4ff88a799", size = 328847, upload-time = "2026-06-11T21:55:34.035Z" }, + { url = "https://files.pythonhosted.org/packages/0f/58/dc12f2cd484951524af6e3382c785869b9b3fb5e52ee95ae23add53ee8f9/protobuf-7.35.1-cp310-abi3-manylinux2014_s390x.whl", hash = "sha256:b73f9489a4b8b1c9cb1f8ed951c736392592edb24b9d6819f36d2e10b171d5b4", size = 344030, upload-time = "2026-06-11T21:55:34.941Z" }, + { url = "https://files.pythonhosted.org/packages/e4/be/5b3cfe508bfab6761414ff944e3366eb13be4fd71efcd69450f89ba39f43/protobuf-7.35.1-cp310-abi3-manylinux2014_x86_64.whl", hash = "sha256:74758715c53d7158fb76caf4f0cfdacc5329a4b1bb994f865d6cf302d413a1c4", size = 327130, upload-time = "2026-06-11T21:55:35.921Z" }, + { url = "https://files.pythonhosted.org/packages/d8/bc/6d6c7ba8709c85f8f2c390b2b118d6fb08a783676a572271851bf45a7d22/protobuf-7.35.1-cp310-abi3-win32.whl", hash = "sha256:353652e4efd0bca5b5fc2656abf8307ef351f0cf938c9eba09f0e09c20a25c30", size = 428945, upload-time = "2026-06-11T21:55:37.034Z" }, + { url = "https://files.pythonhosted.org/packages/0a/19/8d0cb6f20a1ef7b18f1c8986ad5783f22f84cce39c6ce9a6e645ea55192e/protobuf-7.35.1-cp310-abi3-win_amd64.whl", hash = "sha256:230a75ddfc2de4806e56696ce9640c1cdfdb6543b7cfce98d42a4c0a0e7bdb87", size = 439996, upload-time = "2026-06-11T21:55:38.123Z" }, + { url = "https://files.pythonhosted.org/packages/19/c7/5f7c636ec43e0c545e28d1f1db71990108306f7bdcb89f069ba97e428e7f/protobuf-7.35.1-py3-none-any.whl", hash = "sha256:4bc97768d8fe4ad6743c8a19403e314511ed9f6d13205b687e52421c023ac1b9", size = 171659, upload-time = "2026-06-11T21:55:39.155Z" }, ] [[package]] @@ -1774,7 +1774,7 @@ wheels = [ [[package]] name = "transformers" -version = "5.11.0" +version = "5.12.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "huggingface-hub" }, @@ -1787,9 +1787,9 @@ dependencies = [ { name = "tqdm" }, { name = "typer" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7d/4a/2ee05f9a06bb2dd34cac951548553738a826762fb7adfa7ce256daeaeac8/transformers-5.11.0.tar.gz", hash = "sha256:1dbecade5b8a09bdf3e9e8fdfc9d312cb9eccf5a201080dc894b373f0f3eb5f4", size = 8874363, upload-time = "2026-06-10T16:31:52.441Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0c/f9/4552e2ba55db1c943aea0d4c09a32e9cbe5445b9eabe9856900de503dc8f/transformers-5.12.0.tar.gz", hash = "sha256:f0cf42ae1464c2eb41e7e0e66d7fd4b66145f48af17093b4cc0b2e9781faa7f4", size = 8923020, upload-time = "2026-06-12T14:39:20.43Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/40/30fdefda9b8aff8d36054dbb37d8e1464cc1130c29623403c3c2147bdee1/transformers-5.11.0-py3-none-any.whl", hash = "sha256:06d0a34eab529955f8e704b530663c7d6bd6f9c7a9e47a05cddb4a44e7b6566b", size = 11094597, upload-time = "2026-06-10T16:31:49.08Z" }, + { url = "https://files.pythonhosted.org/packages/ff/1f/d385913c38e900d23b728a4188fee625f2fccb306aeebc59be9d91404a5a/transformers-5.12.0-py3-none-any.whl", hash = "sha256:500be9eb644ede81c3103eee7687fc36d05dd75d1c76686c3820b26396fe7c7c", size = 11150246, upload-time = "2026-06-12T14:39:17.009Z" }, ] [[package]] @@ -1881,7 +1881,7 @@ wheels = [ [[package]] name = "winnow" -version = "0.2.11" +version = "0.2.13" source = { editable = "." } dependencies = [ { name = "croniter" }, diff --git a/uv.lock b/uv.lock index 6579e02..7fc7c87 100644 --- a/uv.lock +++ b/uv.lock @@ -1970,15 +1970,16 @@ version = "2.12.0" source = { registry = "https://download.pytorch.org/whl/cpu" } resolution-markers = [ "platform_machine != 's390x' and sys_platform == 'darwin'", + "platform_machine == 's390x' and sys_platform == 'darwin'", ] dependencies = [ - { name = "filelock", marker = "(platform_machine != 's390x' and sys_platform == 'darwin') or (platform_machine == 's390x' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform != 'darwin' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, - { name = "fsspec", marker = "(platform_machine != 's390x' and sys_platform == 'darwin') or (platform_machine == 's390x' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform != 'darwin' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, - { name = "jinja2", marker = "(platform_machine != 's390x' and sys_platform == 'darwin') or (platform_machine == 's390x' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform != 'darwin' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, - { name = "networkx", marker = "(platform_machine != 's390x' and sys_platform == 'darwin') or (platform_machine == 's390x' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform != 'darwin' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, - { name = "setuptools", marker = "(platform_machine != 's390x' and sys_platform == 'darwin') or (platform_machine == 's390x' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform != 'darwin' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, - { name = "sympy", marker = "(platform_machine != 's390x' and sys_platform == 'darwin') or (platform_machine == 's390x' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform != 'darwin' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, - { name = "typing-extensions", marker = "(platform_machine != 's390x' and sys_platform == 'darwin') or (platform_machine == 's390x' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform != 'darwin' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, + { name = "filelock", marker = "sys_platform == 'darwin' or (extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, + { name = "fsspec", marker = "sys_platform == 'darwin' or (extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, + { name = "jinja2", marker = "sys_platform == 'darwin' or (extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, + { name = "networkx", marker = "sys_platform == 'darwin' or (extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, + { name = "setuptools", marker = "sys_platform == 'darwin' or (extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, + { name = "sympy", marker = "sys_platform == 'darwin' or (extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, + { name = "typing-extensions", marker = "sys_platform == 'darwin' or (extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, ] wheels = [ { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.12.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:90dd587a5f61bfe1307148b581e2084fc5bc4a06e2b90a20e9a36b81087ff16b", upload-time = "2026-05-12T16:20:17Z" }, @@ -2041,16 +2042,15 @@ resolution-markers = [ "platform_machine == 's390x' and sys_platform == 'win32'", "platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'", "platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'", - "platform_machine == 's390x' and sys_platform == 'darwin'", ] dependencies = [ - { name = "filelock", marker = "(platform_machine == 's390x' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux') or (sys_platform == 'darwin' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform == 'linux' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, - { name = "fsspec", marker = "(platform_machine == 's390x' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux') or (sys_platform == 'darwin' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform == 'linux' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, - { name = "jinja2", marker = "(platform_machine == 's390x' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux') or (sys_platform == 'darwin' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform == 'linux' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, - { name = "networkx", marker = "(platform_machine == 's390x' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux') or (sys_platform == 'darwin' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform == 'linux' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, - { name = "setuptools", marker = "(platform_machine == 's390x' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux') or (sys_platform == 'darwin' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform == 'linux' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, - { name = "sympy", marker = "(platform_machine == 's390x' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux') or (sys_platform == 'darwin' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform == 'linux' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, - { name = "typing-extensions", marker = "(platform_machine == 's390x' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux') or (sys_platform == 'darwin' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform == 'linux' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, + { name = "filelock", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux') or (sys_platform == 'darwin' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform == 'linux' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, + { name = "fsspec", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux') or (sys_platform == 'darwin' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform == 'linux' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, + { name = "jinja2", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux') or (sys_platform == 'darwin' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform == 'linux' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, + { name = "networkx", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux') or (sys_platform == 'darwin' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform == 'linux' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, + { name = "setuptools", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux') or (sys_platform == 'darwin' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform == 'linux' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, + { name = "sympy", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux') or (sys_platform == 'darwin' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform == 'linux' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, + { name = "typing-extensions", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux') or (sys_platform == 'darwin' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform == 'linux' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, ] wheels = [ { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.12.0%2Bcpu-cp313-cp313-linux_s390x.whl", hash = "sha256:5e0da19e1c3bfdc9b92638c552579eac678354485d61fc8921b0461fd6c40449", upload-time = "2026-05-12T23:17:05Z" }, @@ -2116,11 +2116,12 @@ version = "0.27.0" source = { registry = "https://download.pytorch.org/whl/cpu" } resolution-markers = [ "platform_machine != 's390x' and sys_platform == 'darwin'", + "platform_machine == 's390x' and sys_platform == 'darwin'", ] dependencies = [ - { name = "numpy", marker = "(platform_machine != 's390x' and sys_platform == 'darwin') or (platform_machine == 's390x' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform != 'darwin' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, - { name = "pillow", marker = "(platform_machine != 's390x' and sys_platform == 'darwin') or (platform_machine == 's390x' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform != 'darwin' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, - { name = "torch", version = "2.12.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine != 's390x' and sys_platform == 'darwin') or (platform_machine == 's390x' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform != 'darwin' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, + { name = "numpy", marker = "sys_platform == 'darwin' or (extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, + { name = "pillow", marker = "sys_platform == 'darwin' or (extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, + { name = "torch", version = "2.12.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'darwin' or (extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, ] wheels = [ { url = "https://download-r2.pytorch.org/whl/cpu/torchvision-0.27.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:41d6dae73e1af09fa82ded597ae57f2a2314285acde54b25890a8f8e51b999d7", upload-time = "2026-05-12T16:20:37Z" }, @@ -2171,12 +2172,11 @@ resolution-markers = [ "platform_machine == 's390x' and sys_platform == 'win32'", "platform_machine != 's390x' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'", "platform_machine == 's390x' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'", - "platform_machine == 's390x' and sys_platform == 'darwin'", ] dependencies = [ - { name = "numpy", marker = "(platform_machine == 's390x' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux') or (sys_platform == 'darwin' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform == 'linux' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, - { name = "pillow", marker = "(platform_machine == 's390x' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux') or (sys_platform == 'darwin' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform == 'linux' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, - { name = "torch", version = "2.12.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine == 's390x' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux') or (sys_platform == 'darwin' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform == 'linux' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, + { name = "numpy", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux') or (sys_platform == 'darwin' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform == 'linux' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, + { name = "pillow", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux') or (sys_platform == 'darwin' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform == 'linux' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, + { name = "torch", version = "2.12.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux') or (sys_platform == 'darwin' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform == 'linux' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, ] wheels = [ { url = "https://download-r2.pytorch.org/whl/cpu/torchvision-0.27.0%2Bcpu-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:69093b64b2762c43df17be2db2be163029963d90bc3f1801500fdeb723e54833", upload-time = "2026-05-12T16:20:36Z" }, @@ -2306,13 +2306,13 @@ dependencies = [ { name = "pyyaml" }, { name = "requests" }, { name = "scipy" }, - { name = "torch", version = "2.12.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine != 's390x' and sys_platform == 'darwin') or (platform_machine == 's390x' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform != 'darwin' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, + { name = "torch", version = "2.12.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'darwin' or (extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, { name = "torch", version = "2.12.0", source = { registry = "https://pypi.org/simple" }, marker = "(platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (platform_machine == 'aarch64' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (platform_machine == 'x86_64' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform != 'linux' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, - { name = "torch", version = "2.12.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine == 's390x' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux') or (sys_platform == 'darwin' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform == 'linux' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, + { name = "torch", version = "2.12.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux') or (sys_platform == 'darwin' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform == 'linux' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, { name = "torch", version = "2.12.0+cu126", source = { registry = "https://download.pytorch.org/whl/cu126" }, marker = "(platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine != 'x86_64' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform != 'linux' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, - { name = "torchvision", version = "0.27.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine != 's390x' and sys_platform == 'darwin') or (platform_machine == 's390x' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform != 'darwin' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, + { name = "torchvision", version = "0.27.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'darwin' or (extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, { name = "torchvision", version = "0.27.0", source = { registry = "https://pypi.org/simple" }, marker = "(platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (platform_machine == 'aarch64' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (platform_machine == 'x86_64' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform != 'linux' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, - { name = "torchvision", version = "0.27.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine == 's390x' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux') or (sys_platform == 'darwin' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform == 'linux' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, + { name = "torchvision", version = "0.27.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux') or (sys_platform == 'darwin' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform == 'linux' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, { name = "torchvision", version = "0.27.0+cu126", source = { registry = "https://download.pytorch.org/whl/cu126" }, marker = "(platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine != 'x86_64' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform != 'linux' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, { name = "ultralytics-thop" }, ] @@ -2327,9 +2327,9 @@ version = "2.0.20" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, - { name = "torch", version = "2.12.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine != 's390x' and sys_platform == 'darwin') or (platform_machine == 's390x' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform != 'darwin' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, + { name = "torch", version = "2.12.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'darwin' or (extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, { name = "torch", version = "2.12.0", source = { registry = "https://pypi.org/simple" }, marker = "(platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (platform_machine == 'aarch64' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (platform_machine == 'x86_64' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform != 'linux' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, - { name = "torch", version = "2.12.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine == 's390x' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux') or (sys_platform == 'darwin' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform == 'linux' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, + { name = "torch", version = "2.12.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux') or (sys_platform == 'darwin' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform == 'linux' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, { name = "torch", version = "2.12.0+cu126", source = { registry = "https://download.pytorch.org/whl/cu126" }, marker = "(platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine != 'x86_64' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform != 'linux' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/98/c6/d25cb53e141242f74950744179c21b8798bb09b9e7161465ecda4f577ddf/ultralytics_thop-2.0.20.tar.gz", hash = "sha256:f3595e0d8c6fd0b9f62fc2cd9be921755e2649a05c34f1fabaea0bff7295d641", size = 34682, upload-time = "2026-06-06T11:42:42.184Z" } @@ -2348,7 +2348,7 @@ wheels = [ [[package]] name = "winnow" -version = "0.2.13" +version = "0.3.0" source = { editable = "." } dependencies = [ { name = "croniter" }, @@ -2363,13 +2363,13 @@ dependencies = [ { name = "python-dotenv" }, { name = "requests" }, { name = "rich" }, - { name = "torch", version = "2.12.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine != 's390x' and sys_platform == 'darwin') or (platform_machine == 's390x' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform != 'darwin' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, + { name = "torch", version = "2.12.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'darwin' or (extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, { name = "torch", version = "2.12.0", source = { registry = "https://pypi.org/simple" }, marker = "(platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (platform_machine == 'aarch64' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (platform_machine == 'x86_64' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform != 'linux' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, - { name = "torch", version = "2.12.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine == 's390x' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux') or (sys_platform == 'darwin' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform == 'linux' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, + { name = "torch", version = "2.12.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux') or (sys_platform == 'darwin' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform == 'linux' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, { name = "torch", version = "2.12.0+cu126", source = { registry = "https://download.pytorch.org/whl/cu126" }, marker = "(platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine != 'x86_64' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform != 'linux' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, - { name = "torchvision", version = "0.27.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine != 's390x' and sys_platform == 'darwin') or (platform_machine == 's390x' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform != 'darwin' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, + { name = "torchvision", version = "0.27.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'darwin' or (extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, { name = "torchvision", version = "0.27.0", source = { registry = "https://pypi.org/simple" }, marker = "(platform_machine != 'aarch64' and platform_machine != 'x86_64' and sys_platform == 'linux') or (platform_machine == 'aarch64' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (platform_machine == 'x86_64' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform != 'linux' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, - { name = "torchvision", version = "0.27.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine == 's390x' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux') or (sys_platform == 'darwin' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform == 'linux' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, + { name = "torchvision", version = "0.27.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux') or (sys_platform == 'darwin' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform == 'linux' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, { name = "torchvision", version = "0.27.0+cu126", source = { registry = "https://download.pytorch.org/whl/cu126" }, marker = "(platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine != 'x86_64' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu') or (sys_platform != 'linux' and extra == 'project-11-onnxruntime' and extra == 'project-15-onnxruntime-gpu')" }, { name = "transformers" }, { name = "ultralytics" }, From 870560de1a8c7227a8e2367e85ede8117510b42b Mon Sep 17 00:00:00 2001 From: Holden Date: Sat, 13 Jun 2026 14:23:33 +0000 Subject: [PATCH 10/10] Fix CI: lint line-length, lockfile detached HEAD, release disk exhaustion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - quality.py:122: split long tuple line to satisfy E501 (146 → ≤120) - update-lockfile.yml: add branches filter so tag pushes don't trigger the workflow (tag checkout is detached HEAD; git push has no target) - release.yml: split four Docker build steps into parallel jobs (build-gpu, build-cpu, build-rocm, build-intel), each with its own runner; previously all four ran in one job and exhausted disk after GPU+CPU builds, leaving ROCm and Intel cancelled Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/release.yml | 145 +++++++++++++++++++++++--- .github/workflows/update-lockfile.yml | 2 + winnow/quality.py | 5 +- 3 files changed, 135 insertions(+), 17 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 56e454f..a684ed2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,11 +15,13 @@ concurrency: jobs: release: - name: Create GitHub Release & Build Image + name: Create GitHub Release runs-on: ubuntu-latest permissions: contents: write - packages: write + outputs: + tag: ${{ steps.tag.outputs.TAG }} + version: ${{ steps.tag.outputs.VERSION }} steps: - name: Free up disk space run: | @@ -60,12 +62,6 @@ jobs: uv lock rm _pyproject_orig.toml - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v4 - - name: Resolve tag name id: tag run: | @@ -131,6 +127,30 @@ jobs: prerelease: false, }); + build-gpu: + name: Build GPU image + needs: release + runs-on: ubuntu-latest + permissions: + packages: write + steps: + - name: Free up disk space + run: | + sudo rm -rf /usr/share/dotnet + sudo rm -rf /opt/ghc + sudo rm -rf "/usr/local/share/boost" + sudo rm -rf "$AGENT_TOOLSDIRECTORY" + echo "Disk space freed." + + - name: Checkout + uses: actions/checkout@v6 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + - name: Log in to GHCR uses: docker/login-action@v4 with: @@ -145,12 +165,43 @@ jobs: file: ./Dockerfile platforms: linux/amd64,linux/arm64 push: true - build-args: VERSION=${{ steps.tag.outputs.VERSION }} + build-args: VERSION=${{ needs.release.outputs.version }} cache-from: type=gha,scope=release-gpu cache-to: type=gha,mode=max,scope=release-gpu tags: | ghcr.io/sudolulo/winnow:latest - ghcr.io/sudolulo/winnow:${{ steps.tag.outputs.TAG }} + ghcr.io/sudolulo/winnow:${{ needs.release.outputs.tag }} + + build-cpu: + name: Build CPU image + needs: release + runs-on: ubuntu-latest + permissions: + packages: write + steps: + - name: Free up disk space + run: | + sudo rm -rf /usr/share/dotnet + sudo rm -rf /opt/ghc + sudo rm -rf "/usr/local/share/boost" + sudo rm -rf "$AGENT_TOOLSDIRECTORY" + echo "Disk space freed." + + - name: Checkout + uses: actions/checkout@v6 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + + - name: Log in to GHCR + uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push CPU image uses: docker/build-push-action@v6 @@ -161,12 +212,43 @@ jobs: push: true build-args: | VARIANT=cpu - VERSION=${{ steps.tag.outputs.VERSION }} + VERSION=${{ needs.release.outputs.version }} cache-from: type=gha,scope=release-cpu cache-to: type=gha,mode=max,scope=release-cpu tags: | ghcr.io/sudolulo/winnow:cpu - ghcr.io/sudolulo/winnow:${{ steps.tag.outputs.TAG }}-cpu + ghcr.io/sudolulo/winnow:${{ needs.release.outputs.tag }}-cpu + + build-rocm: + name: Build ROCm image + needs: release + runs-on: ubuntu-latest + permissions: + packages: write + steps: + - name: Free up disk space + run: | + sudo rm -rf /usr/share/dotnet + sudo rm -rf /opt/ghc + sudo rm -rf "/usr/local/share/boost" + sudo rm -rf "$AGENT_TOOLSDIRECTORY" + echo "Disk space freed." + + - name: Checkout + uses: actions/checkout@v6 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + + - name: Log in to GHCR + uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push ROCm image uses: docker/build-push-action@v6 @@ -177,12 +259,43 @@ jobs: push: true build-args: | VARIANT=rocm - VERSION=${{ steps.tag.outputs.VERSION }} + VERSION=${{ needs.release.outputs.version }} cache-from: type=gha,scope=release-rocm cache-to: type=gha,mode=max,scope=release-rocm tags: | ghcr.io/sudolulo/winnow:rocm - ghcr.io/sudolulo/winnow:${{ steps.tag.outputs.TAG }}-rocm + ghcr.io/sudolulo/winnow:${{ needs.release.outputs.tag }}-rocm + + build-intel: + name: Build Intel image + needs: release + runs-on: ubuntu-latest + permissions: + packages: write + steps: + - name: Free up disk space + run: | + sudo rm -rf /usr/share/dotnet + sudo rm -rf /opt/ghc + sudo rm -rf "/usr/local/share/boost" + sudo rm -rf "$AGENT_TOOLSDIRECTORY" + echo "Disk space freed." + + - name: Checkout + uses: actions/checkout@v6 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + + - name: Log in to GHCR + uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push Intel image uses: docker/build-push-action@v6 @@ -193,9 +306,9 @@ jobs: push: true build-args: | VARIANT=intel - VERSION=${{ steps.tag.outputs.VERSION }} + VERSION=${{ needs.release.outputs.version }} cache-from: type=gha,scope=release-intel cache-to: type=gha,mode=max,scope=release-intel tags: | ghcr.io/sudolulo/winnow:intel - ghcr.io/sudolulo/winnow:${{ steps.tag.outputs.TAG }}-intel + ghcr.io/sudolulo/winnow:${{ needs.release.outputs.tag }}-intel diff --git a/.github/workflows/update-lockfile.yml b/.github/workflows/update-lockfile.yml index b557522..b677152 100644 --- a/.github/workflows/update-lockfile.yml +++ b/.github/workflows/update-lockfile.yml @@ -3,6 +3,8 @@ name: Update lockfiles on: push: + branches: + - '**' paths: - 'pyproject.toml' - 'pyproject-cpu.toml' diff --git a/winnow/quality.py b/winnow/quality.py index 4ccc2d4..8db3ac7 100644 --- a/winnow/quality.py +++ b/winnow/quality.py @@ -119,7 +119,10 @@ def assess_quality( blur_score = float(cv2.Laplacian(gray, cv2.CV_64F).var()) checks = [ - (blur_score >= blur_threshold, f"Blurry (laplacian={blur_score:.1f}, threshold={blur_threshold})" if blur_score < blur_threshold else ""), + ( + blur_score >= blur_threshold, + f"Blurry (laplacian={blur_score:.1f}, threshold={blur_threshold})" if blur_score < blur_threshold else "", + ), check_grayscale(img_np), check_exposure(img_np), check_confidence(confidence, min_confidence),