mirror of
https://github.com/wiltodelta/remove-ai-watermarks.git
synced 2026-10-15 05:50:45 +02:00
Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
804735aa48 | ||
|
|
e3f714dcd2 | ||
|
|
7169780dd0 | ||
|
|
c7021b900b | ||
|
|
3f55df6f99 | ||
|
|
4795149d34 |
No files matched your search
@@ -129,6 +129,10 @@ Do not classify an entire module as untestable because its main path downloads a
|
||||
- the photo-classify gate in `test_classify.py`: DEFINITELY is ridge AND MLP,
|
||||
POSSIBLY does not emit a provider, and `identify` does not import
|
||||
`remove_ai_watermarks.classify`.
|
||||
- the source-export classifier in `test_source_classify.py`: the Hub revision
|
||||
and artifact hash are immutable, the pickle-free schema and exact GELU are
|
||||
validated, threshold mutation changes the real decision, and `identify`
|
||||
never consumes the result as watermark evidence.
|
||||
- the `InvisibleOptions` defaults, in `test_api.py`. When one signature promises to
|
||||
mirror another, compare them field by field rather than pinning the values you happen
|
||||
to know about, so the next field added on one side and not the other fails at the
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
name: Publish source classifier to Hugging Face
|
||||
|
||||
# Manual only. The derivative model weights live in a GitHub model-freeze
|
||||
# release; the private training corpus and caches never enter this repository.
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
confirm:
|
||||
description: "Type publish"
|
||||
required: true
|
||||
weights_tag:
|
||||
description: "GitHub Release tag holding source-pipeline-mlp.npz"
|
||||
required: true
|
||||
default: source-classify-freeze-2026-09-16-r2
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
if: ${{ github.event.inputs.confirm == 'publish' }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- uses: astral-sh/setup-uv@v7
|
||||
with:
|
||||
python-version: "3.12"
|
||||
|
||||
- name: Download derivative model weights
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
TAG: ${{ github.event.inputs.weights_tag }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p weights
|
||||
gh release download "$TAG" \
|
||||
--repo "${{ github.repository }}" \
|
||||
--pattern source-pipeline-mlp.npz \
|
||||
--dir weights
|
||||
test -s weights/source-pipeline-mlp.npz
|
||||
|
||||
- name: Verify the frozen artifact
|
||||
run: |
|
||||
set -euo pipefail
|
||||
expected="$(jq -r .artifact_sha256 docs/source-classify-hf/metrics.json)"
|
||||
test "${#expected}" -eq 64
|
||||
echo "$expected weights/source-pipeline-mlp.npz" | sha256sum --check
|
||||
|
||||
- name: Publish to Hugging Face
|
||||
env:
|
||||
HF_TOKEN: ${{ secrets.HF_TOKEN }}
|
||||
SHA: ${{ github.sha }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [ -z "${HF_TOKEN:-}" ]; then
|
||||
echo "HF_TOKEN secret is not set" >&2
|
||||
exit 1
|
||||
fi
|
||||
uv run --no-project --with huggingface-hub \
|
||||
python scripts/publish_source_classify_hf.py \
|
||||
--src weights \
|
||||
--message "Source-classify freeze (GitHub ${SHA:0:7})"
|
||||
@@ -43,7 +43,7 @@ The published Agent Skill is [`skills/remove-ai-watermarks/`](skills/remove-ai-w
|
||||
|
||||
[`docs/module-internals.md`](docs/module-internals.md) is the canonical per-module map, including design decisions, thresholds, calibration history, incident records, and regression guards. Read the relevant section before changing a subsystem.
|
||||
|
||||
Research and current constraints are routed through [`docs/index.md`](docs/index.md), especially [`docs/known-limitations.md`](docs/known-limitations.md), [`docs/supported-signals.md`](docs/supported-signals.md), [`docs/synthid.md`](docs/synthid.md), and [`docs/watermarking-landscape.md`](docs/watermarking-landscape.md). Pixel photo classification is [`docs/photo-classify.md`](docs/photo-classify.md); it is not `identify`. Classifier research is split between general [`AI-generated image classifiers`](docs/ai-generated-image-classifiers.md) and [`SynthID source classifiers`](docs/synthid-classifiers.md). Other SynthID campaign logs are [`docs/synthid-detector-research.md`](docs/synthid-detector-research.md) and [`docs/synthid-removal-research.md`](docs/synthid-removal-research.md). The pre-split chronological archive is [`docs/synthid-detector-removal-plan.md`](docs/synthid-detector-removal-plan.md). The benchmark kernel and its pinned local oracles (AudioSeal, VideoSeal) cover the audio/video watermark cohorts and research studies: [`docs/benchmark-kernel.md`](docs/benchmark-kernel.md).
|
||||
Research and current constraints are routed through [`docs/index.md`](docs/index.md), especially [`docs/known-limitations.md`](docs/known-limitations.md), [`docs/supported-signals.md`](docs/supported-signals.md), [`docs/synthid.md`](docs/synthid.md), and [`docs/watermarking-landscape.md`](docs/watermarking-landscape.md). Pixel photo classification is [`docs/photo-classify.md`](docs/photo-classify.md); the separate OpenAI/Google/unknown export-pipeline signal is [`docs/source-classify.md`](docs/source-classify.md). Neither runs inside `identify` or constitutes SynthID detection. Classifier research is split between general [`AI-generated image classifiers`](docs/ai-generated-image-classifiers.md) and [`SynthID source classifiers`](docs/synthid-classifiers.md). Other SynthID campaign logs are [`docs/synthid-detector-research.md`](docs/synthid-detector-research.md) and [`docs/synthid-removal-research.md`](docs/synthid-removal-research.md). The pre-split chronological archive is [`docs/synthid-detector-removal-plan.md`](docs/synthid-detector-removal-plan.md). The benchmark kernel and its pinned local oracles (AudioSeal, VideoSeal) cover the audio/video watermark cohorts and research studies: [`docs/benchmark-kernel.md`](docs/benchmark-kernel.md).
|
||||
|
||||
## Data safety
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@ specialized Python API can inspect and disrupt the validated local
|
||||
| --- | --- |
|
||||
| Metadata inspection and stripping | `remove-ai-watermarks` |
|
||||
| Photograph AI-versus-camera classification | `remove-ai-watermarks[classify]` |
|
||||
| OpenAI/Google/unknown source-export classification | `remove-ai-watermarks[source-classify]` |
|
||||
| Visible detection and removal | `remove-ai-watermarks[visible]` |
|
||||
| Visible video processing | `remove-ai-watermarks[video]` |
|
||||
| Video SynthID removal | `remove-ai-watermarks[video,diffusion]` |
|
||||
@@ -68,7 +69,7 @@ specialized Python API can inspect and disrupt the validated local
|
||||
| Every production feature available on the active Python | `remove-ai-watermarks[all]` |
|
||||
|
||||
Lower-level and specialized extras include `pixels`, `heif`, `trustmark`,
|
||||
`migan`, `lama`, `diffusion`, and `classify-onnx`. The
|
||||
`migan`, `lama`, `diffusion`, `classify-onnx`, and `source-classify`. The
|
||||
[installation guide](docs/installation.md#feature-extras) documents their exact
|
||||
dependency composition, Python compatibility, and model requirements.
|
||||
|
||||
@@ -96,6 +97,19 @@ remove-ai-watermarks classify image.png
|
||||
|
||||
Guide: [photo pixel classification](docs/photo-classify.md).
|
||||
|
||||
For a lightweight, abstaining OpenAI/Google/unknown source-export signal after
|
||||
metadata removal, use the separate Python API. It is not a SynthID detector:
|
||||
|
||||
```python
|
||||
import remove_ai_watermarks as raiw
|
||||
|
||||
result = raiw.classify_source("image.png")
|
||||
print(result.label, result.reason)
|
||||
```
|
||||
|
||||
Install `remove-ai-watermarks[source-classify]`. Guide:
|
||||
[source-pipeline classification](docs/source-classify.md).
|
||||
|
||||
Signed provenance is the supported route for SynthID and `identify` reads it.
|
||||
There is no local SynthID pixel detector in the package. Research on a
|
||||
periodic lattice expert is in `scripts/synthid_runtime/` and
|
||||
|
||||
@@ -13,6 +13,7 @@ to run the tool. Use the maintainer references only when changing the code.
|
||||
| [Supported signals](supported-signals.md) | You need to know which visible marks, metadata formats, and invisible signals are covered. |
|
||||
| [Known limitations](known-limitations.md) | You need the quality, device, format, or verification boundaries. |
|
||||
| [Photo pixel classification](photo-classify.md) | You want `classify` / `classify_pixels`: AI versus camera, optional provider. |
|
||||
| [Source-pipeline classification](source-classify.md) | You want an abstaining OpenAI/Google/unknown export-pipeline signal after metadata removal. |
|
||||
| [Photo classifier training](photo-classify-training.md) | You need the retrain pack, public sources, and what is not published. |
|
||||
| [Scope, safety, and legal notes](legal-and-safety.md) | You need the intended use and legal context. |
|
||||
|
||||
@@ -42,6 +43,7 @@ The current behavior is defined by the code, tests, README, and user guides.
|
||||
- [AI-generated image classifiers](ai-generated-image-classifiers.md) (photo AI-versus-camera freeze consumed by `classify_pixels`, not `identify`)
|
||||
- [Photo classifier Hugging Face card](photo-classify-hf/README.md) (Hub publication text of the 2026-08-31 freeze)
|
||||
- [SynthID source classifiers](synthid-classifiers.md) (metadata-free OpenAI/Gemini source finding and provider-lineage experiments)
|
||||
- [Source-pipeline classifier model card](source-classify-hf/README.md) (published derivative weights, aggregate evaluation, and intended-use boundary)
|
||||
- [SynthID local detector research](synthid-detector-research.md)
|
||||
- [SynthID mark removal research](synthid-removal-research.md)
|
||||
- [SynthID identity research](synthid-robust-identity-research.md)
|
||||
|
||||
@@ -129,6 +129,7 @@ application actually uses:
|
||||
| `trustmark` | Adobe TrustMark detection on Python 3.11-3.12 | trustmark | Yes |
|
||||
| `classify` | Metadata-free photo AI-versus-camera classifier plus gated provider | `pixels`, Torch, Transformers | Yes |
|
||||
| `classify-onnx` | Optional CPU-only ONNX vision runtime for `classify` | `classify`, ONNX Runtime | Yes |
|
||||
| `source-classify` | Abstaining OpenAI/Google/unknown source-export classifier, not SynthID detection | `pixels`, Hugging Face Hub | Model download, no Torch |
|
||||
| `diffusion` | Torch and Diffusers runtime; video SynthID regeneration | `pixels`, Torch, Diffusers | Yes |
|
||||
| `migan` | MI-GAN ONNX fill backend | `visible`, ONNX Runtime | Model download, no Torch |
|
||||
| `lama` | big-LaMa ONNX fill backend | `visible`, ONNX Runtime | Model download, no Torch |
|
||||
@@ -156,6 +157,7 @@ flowchart LR
|
||||
trustmark
|
||||
classify --> pixels
|
||||
onnx["classify-onnx"] --> classify
|
||||
source["source-classify"] --> pixels
|
||||
```
|
||||
|
||||
`heif`, `trustmark`, and `text-draft` are independent branches. Combine them
|
||||
|
||||
@@ -20,6 +20,14 @@ The command is never started by `identify`. A no-signal provenance result stays
|
||||
unknown. This is not a clean verdict and it does not run cleanup. Full guide:
|
||||
[photo pixel classification](photo-classify.md).
|
||||
|
||||
`classify_source` is a different, lightweight OpenAI/Google/unknown
|
||||
source-export classifier. It is useful only while the pixels still resemble an
|
||||
original export. A 95% resize reduced measured Google recall to 1%, and a JPEG
|
||||
quality-75 round trip made every public evaluation row abstain. Its provider
|
||||
label is not evidence that SynthID is present, and `unknown` is not evidence
|
||||
that SynthID is absent. It is also never started by `identify`. Full guide:
|
||||
[source-pipeline classification](source-classify.md).
|
||||
|
||||
## Visible removal
|
||||
|
||||
### Fill quality depends on the background
|
||||
|
||||
@@ -1116,6 +1116,30 @@ both backends explicitly (`tests/test_classify.py` pins the seam). User guide:
|
||||
[photo-classify.md](photo-classify.md). Hub card:
|
||||
[photo-classify-hf/README.md](photo-classify-hf/README.md).
|
||||
|
||||
### Source-pipeline classifier
|
||||
|
||||
[`source_classify.py`](../src/remove_ai_watermarks/source_classify.py) is a
|
||||
separate, lightweight OpenAI/Google/unknown source-export classifier. It fuses
|
||||
the existing 124-dimensional patch descriptor with the 768-dimensional
|
||||
phase-free spectrum in
|
||||
[`_internal/source_spectral.py`](../src/remove_ai_watermarks/_internal/source_spectral.py),
|
||||
then evaluates the frozen 892 -> 128 -> 64 -> 3 GELU MLP in NumPy. Provider
|
||||
labels clear independent calibrated margins; every other row is `unknown`.
|
||||
The NumPy decision treats values within `1e-5` of the stored margin as the
|
||||
inclusive training threshold, absorbing float32 BLAS differences; this matches
|
||||
the frozen training-runtime decisions on all 27,811 cached internal and
|
||||
external feature rows.
|
||||
|
||||
The runtime pins both the immutable Hub revision and the NPZ SHA-256, loads
|
||||
with `allow_pickle=False`, and rejects any field, shape, class, activation, or
|
||||
non-finite-value drift. `RAIW_SOURCE_CLASSIFY_WEIGHTS` is the offline override.
|
||||
The `source-classify` extra adds `pixels` and Hugging Face Hub but no Torch or
|
||||
Transformers. `identify` never imports or runs this path. Its output names a
|
||||
complete source/export pipeline and is not SynthID detection. Unit contracts
|
||||
and the threshold mutation guard live in `tests/test_source_classify.py`;
|
||||
evaluation and attack limits live in
|
||||
[`synthid-classifiers.md`](synthid-classifiers.md).
|
||||
|
||||
The CLIP loader suppresses discarded random parameter initialization with
|
||||
Transformers' `no_init_weights` context before applying the complete frozen
|
||||
state dict. Normal `CLIPModel(config)` construction initialized 427 million
|
||||
|
||||
+18
-1
@@ -6,12 +6,29 @@ and pipeline modules are intended for maintainers and specialized workflows.
|
||||
Dependency groups are identical for the CLI and Python API. The default install
|
||||
covers metadata extraction, normalization, verdict logic, and stripping.
|
||||
Array/pixel APIs use `pixels`; visible removal uses `visible`; DWT-DCT detection
|
||||
uses `detect`; pixel photo classification uses `classify`; invisible image removal uses `qwen-zimage` and an NVIDIA GPU; and
|
||||
uses `detect`; pixel photo classification uses `classify`; lightweight source-pipeline classification uses `source-classify`; invisible image removal uses `qwen-zimage` and an NVIDIA GPU; and
|
||||
visible video processing uses `video`. The video-pixel SynthID-removal profile is
|
||||
a separate VAE path that still runs on CPU and combines `video` and `diffusion`. Add `heif`
|
||||
independently when path-based pixel APIs must decode HEIC, HEIF, or AVIF. See
|
||||
the complete [feature-extra matrix](installation.md#feature-extras).
|
||||
|
||||
## Classify a source/export pipeline
|
||||
|
||||
Install `remove-ai-watermarks[source-classify]`, then call the explicit API:
|
||||
|
||||
```python
|
||||
import remove_ai_watermarks as raiw
|
||||
|
||||
result = raiw.classify_source("metadata-stripped.png")
|
||||
print(result.label) # "openai" | "google" | "unknown"
|
||||
print(result.reason) # "classified" | "abstained" | "feature_unavailable"
|
||||
```
|
||||
|
||||
This signal classifies a complete image-generation and export pipeline. It is
|
||||
not a SynthID detector and does not establish watermark presence or absence.
|
||||
See [source-pipeline classification](source-classify.md) for the result fields,
|
||||
model pin, evaluation boundary, and offline weights override.
|
||||
|
||||
## Remove visible marks
|
||||
|
||||
Install `remove-ai-watermarks[visible]` before using the visible-removal API.
|
||||
|
||||
@@ -92,7 +92,17 @@ alone does not upload new heads -- bump the pinned Hub revision when a new
|
||||
head must reach installed clients. Pin a Hub revision in production so a
|
||||
later freeze cannot silently change installed clients.
|
||||
|
||||
The workflow can also be started manually with an optional version input.
|
||||
The separate `wiltodelta/raiw-source-classify` model is published with
|
||||
`.github/workflows/publish-source-classify-hf.yml`. The workflow downloads the
|
||||
derivative `source-pipeline-mlp.npz` artifact from the declared GitHub model
|
||||
freeze, verifies its SHA-256, and uploads only that file plus the tracked card
|
||||
and aggregate metrics under `docs/source-classify-hf/`. The private corpus,
|
||||
catalog, paths, hashes, embeddings, and feature caches are outside this flow.
|
||||
|
||||
The workflow is manual and requires the model-freeze release tag plus an
|
||||
explicit `publish` confirmation. Keep model-freeze GitHub releases marked as
|
||||
pre-releases so a model artifact cannot replace the latest package release in
|
||||
GitHub's release UI.
|
||||
|
||||
If a distribution job fails because a repository or Hugging Face credential is
|
||||
invalid, rotate the corresponding GitHub secret and rerun the failed job. A
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
---
|
||||
license: apache-2.0
|
||||
library_name: numpy
|
||||
pipeline_tag: image-classification
|
||||
tags:
|
||||
- image-forensics
|
||||
- source-attribution
|
||||
- open-set-classification
|
||||
---
|
||||
|
||||
# RAIW source-pipeline classifier
|
||||
|
||||
This is a compact, abstaining pixel-forensics classifier for three labels:
|
||||
`openai`, `google`, and `unknown`. It is intended for original-export-looking
|
||||
images whose metadata may have been removed without changing the pixels.
|
||||
|
||||
This model is **not a SynthID detector**. It does not decode or verify a
|
||||
watermark payload. Its labels describe complete generation and export
|
||||
pipelines, so renderer, geometry, encoding, post-processing, and watermark
|
||||
state may be confounded. `unknown` does not mean that SynthID is absent, and a
|
||||
provider label does not prove that SynthID is present.
|
||||
|
||||
## Model
|
||||
|
||||
- Input: a 124-dimensional residual/spectral forensic feature vector plus a
|
||||
768-dimensional phase-free spectral vector.
|
||||
- Head: 892 -> 128 -> 64 -> 3 MLP with GELU activations.
|
||||
- Output: `openai`, `google`, or abstain as `unknown` using independent
|
||||
provider margins.
|
||||
- Artifact: `source-pipeline-mlp.npz`
|
||||
- SHA-256: `6430b0738869ff07b6845b7c6149c2a83bc3f3785878082886c241c6f7ca0c4a`
|
||||
|
||||
The artifact contains only learned parameters, normalization constants,
|
||||
labels, and thresholds. It does not contain training images, paths, image
|
||||
hashes, embeddings, or a training catalog.
|
||||
|
||||
## Evaluation
|
||||
|
||||
The operating point was selected on an internal calibration split. The locked
|
||||
internal test and the public `bigdatamark/synthid-research` dataset were not
|
||||
used for architecture, checkpoint, or threshold selection.
|
||||
|
||||
| Evaluation | Class | Recall | Precision |
|
||||
|---|---|---:|---:|
|
||||
| Locked internal test (5,369 images) | OpenAI | 60.4% | 84.9% |
|
||||
| Locked internal test (5,369 images) | Google | 57.6% | 87.9% |
|
||||
| Locked internal test (5,369 images) | Unknown | 98.6% | 93.7% |
|
||||
| Public external originals (900 images) | OpenAI | 68.7% | 100.0% |
|
||||
| Public external originals (900 images) | Google | 81.3% | 98.8% |
|
||||
| Public external originals (900 images) | Unknown/Flux | 99.0% | 66.4% |
|
||||
|
||||
On the public external set, a 95% resize reduced Google recall to 1.0%, and a
|
||||
JPEG quality-75 round trip caused the model to abstain on every image. These
|
||||
are expected abstentions, not evidence that a watermark was removed.
|
||||
|
||||
## Intended use
|
||||
|
||||
Use this model as one probabilistic signal in a broader forensic report or to
|
||||
suggest an appropriate processing path. Do not use it to claim watermark
|
||||
presence, authorship, policy compliance, fraud, or provenance. Applications
|
||||
should expose the abstention and the model's narrow source-pipeline claim.
|
||||
|
||||
Feature extraction and inference are versioned in the
|
||||
`remove-ai-watermarks` Python project rather than in this model repository.
|
||||
Use its explicit `classify_source` API; `identify` does not run this model.
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"claim": "abstaining OpenAI/Google source-pipeline classification; not SynthID detection",
|
||||
"artifact_sha256": "6430b0738869ff07b6845b7c6149c2a83bc3f3785878082886c241c6f7ca0c4a",
|
||||
"classes": ["openai", "google", "unknown"],
|
||||
"locked_internal_test": {
|
||||
"n": 5369,
|
||||
"confusion_actual_by_predicted": [[230, 6, 145], [0, 217, 160], [41, 24, 4546]],
|
||||
"recall": {"openai": 0.6036745407, "google": 0.5755968170, "unknown": 0.9859032748},
|
||||
"precision": {"openai": 0.8487084871, "google": 0.8785425101, "unknown": 0.9371263657}
|
||||
},
|
||||
"public_external_originals": {
|
||||
"dataset": "bigdatamark/synthid-research",
|
||||
"n": 900,
|
||||
"confusion_actual_by_predicted": [[206, 0, 94], [0, 244, 56], [0, 3, 297]],
|
||||
"recall": {"openai": 0.6866666667, "google": 0.8133333333, "unknown": 0.99},
|
||||
"precision": {"openai": 1.0, "google": 0.9878542510, "unknown": 0.6644295302}
|
||||
},
|
||||
"selection": {
|
||||
"minimum_calibration_precision": {"openai": 0.99, "google": 0.95},
|
||||
"maximum_calibration_unknown_false_positive_rate": 0.01
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
# Source-pipeline classification
|
||||
|
||||
`classify_source` is an opt-in, metadata-free pixel classifier for images that
|
||||
still resemble an original provider export. It returns `openai`, `google`, or
|
||||
an explicit `unknown` abstention.
|
||||
|
||||
This is not a SynthID detector. It does not decode or verify a watermark
|
||||
payload, and it must not be used to claim that SynthID is present or absent.
|
||||
The model describes the complete source/export pipeline, where renderer,
|
||||
geometry, encoding, post-processing, and watermark state can be confounded.
|
||||
|
||||
Install the lightweight runtime:
|
||||
|
||||
```bash
|
||||
uv add "remove-ai-watermarks[source-classify]"
|
||||
```
|
||||
|
||||
Call the Python API explicitly:
|
||||
|
||||
```python
|
||||
import remove_ai_watermarks as raiw
|
||||
|
||||
result = raiw.classify_source("metadata-stripped.png")
|
||||
print(result.label) # "openai" | "google" | "unknown"
|
||||
print(result.reason) # "classified" | "abstained" | "feature_unavailable"
|
||||
print(result.margins) # provider margins before thresholding
|
||||
print(result.thresholds) # frozen provider thresholds
|
||||
```
|
||||
|
||||
`feature_unavailable` is returned for images below the required 256x256
|
||||
geometry or for a degenerate feature vector. `abstained` means neither provider
|
||||
cleared its independent calibrated margin. Both are honest `unknown` results.
|
||||
|
||||
The first call downloads the exact pinned revision of
|
||||
[`wiltodelta/raiw-source-classify`](https://huggingface.co/wiltodelta/raiw-source-classify).
|
||||
Set `RAIW_SOURCE_CLASSIFY_WEIGHTS` to the NPZ itself or a directory containing
|
||||
`source-pipeline-mlp.npz` for an offline deployment.
|
||||
|
||||
The model is useful as a routing suggestion for a processing workflow or as one
|
||||
probabilistic signal in a broader forensic report. Do not use it as evidence of
|
||||
authorship, fraud, policy compliance, provenance, or watermark presence. The
|
||||
published model card records the evaluation and robustness limits.
|
||||
@@ -121,6 +121,10 @@ entries enter the watermark inventory, while a fingerprint remains a
|
||||
re-linkability warning, never selects pixel regeneration, and does not suppress
|
||||
independent SynthID evidence.
|
||||
|
||||
The opt-in `classify_source` API is not an `identify` signal. It can suggest an
|
||||
OpenAI or Google source/export pipeline after metadata removal, but it neither
|
||||
detects nor decodes a watermark and never changes the provenance verdict.
|
||||
|
||||
## File and container formats
|
||||
|
||||
Pixel based image commands discover these extensions:
|
||||
|
||||
@@ -112,6 +112,41 @@ and a causal marked/unmarked contrast. Mark's same-prompt triples remain an
|
||||
external source/export test, not evidence that the fitted features read the
|
||||
SynthID payload.
|
||||
|
||||
### Full-corpus source-pipeline model, 2026-09-16
|
||||
|
||||
The next experiment trained a compact nonlinear head on the complete available
|
||||
private corpus, including target-provider exports, photographs, and foreign
|
||||
generator negatives. The corpus itself is not published. The released
|
||||
`source-pipeline-mlp.npz` is a derivative artifact containing only learned
|
||||
parameters, normalization constants, labels, and abstention thresholds; it
|
||||
contains no images, paths, image hashes, embeddings, or training catalog.
|
||||
|
||||
The model joins the 124-dimensional forensic descriptor with the
|
||||
768-dimensional phase-free spectral descriptor and applies a
|
||||
892 -> 128 -> 64 -> 3 GELU MLP. Checkpoint and thresholds were selected on a
|
||||
separate calibration partition. The 5,369-image hash-disjoint locked test and
|
||||
Mark's public 900-image originals were not used for architecture, checkpoint,
|
||||
or threshold selection.
|
||||
|
||||
| Evaluation | Class | Recall | Precision |
|
||||
| --- | --- | ---: | ---: |
|
||||
| Locked internal test (5,369 images) | OpenAI | 60.4% | 84.9% |
|
||||
| Locked internal test (5,369 images) | Google | 57.6% | 87.9% |
|
||||
| Locked internal test (5,369 images) | Unknown | 98.6% | 93.7% |
|
||||
| Mark public originals (900 images) | OpenAI | 68.7% | 100.0% |
|
||||
| Mark public originals (900 images) | Google | 81.3% | 98.8% |
|
||||
| Mark public originals (900 images) | Unknown/FLUX | 99.0% | 66.4% |
|
||||
|
||||
A 95% resize reduced Google recall on the public set to 1%, and a JPEG
|
||||
quality-75 round trip made every row abstain. This behavior matches the narrow
|
||||
claim: the model classifies original-looking source/export pipelines after
|
||||
metadata removal. It neither decodes nor detects SynthID, and its `unknown`
|
||||
label does not mean that SynthID is absent. The model card and aggregate
|
||||
metrics are tracked under
|
||||
[`docs/source-classify-hf/`](source-classify-hf/README.md). Runtime integration
|
||||
is provided by the explicit `classify_source` library API and never feeds the
|
||||
`identify` watermark verdict.
|
||||
|
||||
Krawetz's Gemini-chat TPR critique is a verifier-quality claim, not a
|
||||
feature we can ship. [Lead Stories, 2026-07](https://leadstories.com/analysis/2026/07/google-gemini-synthid-detector-confuses-results-within-same-chat.html)
|
||||
documented Gemini repeating the first file's SynthID verdict inside a
|
||||
|
||||
+9
-2
@@ -4,7 +4,7 @@
|
||||
# on it, including the ComfyUI node package. The console script below carries
|
||||
# the same weight, since users have it on PATH.
|
||||
name = "remove-ai-watermarks"
|
||||
version = "0.40.6"
|
||||
version = "0.41.1"
|
||||
description = "AI watermark remover for visible, invisible, and provenance marks in images and video"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.11,<3.15"
|
||||
@@ -160,6 +160,13 @@ classify-onnx = [
|
||||
"remove-ai-watermarks[classify]",
|
||||
"onnxruntime>=1.24.0",
|
||||
]
|
||||
# Compact OpenAI/Google/unknown source-export classifier. This is intentionally
|
||||
# separate from the heavier photo AI-versus-camera classifier and is not a
|
||||
# SynthID detector. Weights download on first use.
|
||||
source-classify = [
|
||||
"remove-ai-watermarks[pixels]",
|
||||
"huggingface-hub>=0.20.0",
|
||||
]
|
||||
# Universal region eraser backend -- big-LaMa via onnxruntime (Carve/LaMa-ONNX,
|
||||
# Apache-2.0). CPU, no torch. Model (~200 MB) is downloaded on first use and
|
||||
# cached by huggingface_hub; it is never bundled in this repo. The default cv2
|
||||
@@ -205,7 +212,7 @@ dev = [
|
||||
]
|
||||
# ``qwen-zimage`` already pulls ``diffusion``; naming both would suggest diffusion is
|
||||
# independently sufficient for a removal, which it is not.
|
||||
all = ["remove-ai-watermarks[video,heif,detect,trustmark,text-restoration,migan,classify]"]
|
||||
all = ["remove-ai-watermarks[video,heif,detect,trustmark,text-restoration,migan,classify,source-classify]"]
|
||||
|
||||
[project.scripts]
|
||||
remove-ai-watermarks = "remove_ai_watermarks.cli:main"
|
||||
|
||||
@@ -55,6 +55,7 @@ explicitly names a tracked canonical result.
|
||||
| `watermark_benchmark_real_cohort.py` | Build a publication-cleared, provider-paired and content-stratified real-image cohort. |
|
||||
| `retrain_photo_classify.py` | CPU-retrain the 2026-08-31 photo heads from a sha256-keyed cache pack, no images. |
|
||||
| `publish_photo_classify_hf.py` | Upload the photo-classify card and freeze weights to Hugging Face `wiltodelta/raiw-photo-classify`. Manual; the Action `publish-photo-classify-hf.yml` is the write-token path. |
|
||||
| `publish_source_classify_hf.py` | Upload the derivative source-pipeline model and its public card to Hugging Face `wiltodelta/raiw-source-classify`. Manual; the Action `publish-source-classify-hf.yml` is the write-token path. |
|
||||
| `verify_engine_selection_fixtures.py` | Verify hashes, dimensions, and prompt pairing in the tracked auto-engine content matrix. |
|
||||
|
||||
## Research and diagnostic prototypes
|
||||
@@ -87,5 +88,7 @@ running the sync command without `--check` is the explicit snapshot update.
|
||||
|
||||
`_plain_console.py` provides plain-text fallbacks for Rich output, and
|
||||
`_text_eval.py` contains normalization helpers shared by text-evaluation scripts.
|
||||
`hf_publish.py` owns the common authenticated, allowlisted Hugging Face model
|
||||
repository upload used by the two model publishers.
|
||||
`engine_selection_manifest.py` owns the content-matrix schema and validation
|
||||
shared by its integrity check and the real-image benchmark builder.
|
||||
@@ -0,0 +1,31 @@
|
||||
"""Shared Hugging Face model-repository publication helper."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import Iterable
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def publish_model_folder(
|
||||
stage: Path,
|
||||
*,
|
||||
repo_id: str,
|
||||
token: str,
|
||||
message: str,
|
||||
allow_patterns: Iterable[str],
|
||||
) -> str:
|
||||
"""Upload an explicit file allowlist and return the Hub revision."""
|
||||
from huggingface_hub import HfApi, create_repo
|
||||
|
||||
create_repo(repo_id, repo_type="model", exist_ok=True, private=False, token=token)
|
||||
commit = HfApi(token=token).upload_folder(
|
||||
folder_path=str(stage),
|
||||
repo_id=repo_id,
|
||||
repo_type="model",
|
||||
commit_message=message,
|
||||
allow_patterns=list(allow_patterns),
|
||||
)
|
||||
return getattr(commit, "oid", "") or ""
|
||||
@@ -24,6 +24,8 @@ import shutil
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
from hf_publish import publish_model_folder
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
HUB_REPO = "wiltodelta/raiw-photo-classify"
|
||||
@@ -92,18 +94,13 @@ def stage_weights(dest: Path, src: Path) -> None:
|
||||
|
||||
|
||||
def publish(stage: Path, *, token: str, message: str) -> str:
|
||||
from huggingface_hub import HfApi, create_repo
|
||||
|
||||
create_repo(HUB_REPO, repo_type="model", exist_ok=True, private=False, token=token)
|
||||
api = HfApi(token=token)
|
||||
commit = api.upload_folder(
|
||||
folder_path=str(stage),
|
||||
return publish_model_folder(
|
||||
stage,
|
||||
repo_id=HUB_REPO,
|
||||
repo_type="model",
|
||||
commit_message=message,
|
||||
token=token,
|
||||
message=message,
|
||||
allow_patterns=["README.md", "operating-point.json", *WEIGHT_FILES, GATE_FILE, GATE_STABLE_FILE],
|
||||
)
|
||||
return getattr(commit, "oid", "") or ""
|
||||
|
||||
|
||||
def main() -> int:
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Publish the source-pipeline model to Hugging Face.
|
||||
|
||||
The derivative weight file is downloaded by the GitHub Action from a model
|
||||
freeze release. The private corpus, feature caches, and training catalog never
|
||||
enter this repository or the Hub staging directory.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
from hf_publish import publish_model_folder
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
HUB_REPO = "wiltodelta/raiw-source-classify"
|
||||
MODEL_FILE = "source-pipeline-mlp.npz"
|
||||
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||
CARD_DIR = REPO_ROOT / "docs" / "source-classify-hf"
|
||||
PUBLIC_FILES = ("README.md", "metrics.json", MODEL_FILE)
|
||||
|
||||
|
||||
def stage_release(dest: Path, src: Path) -> None:
|
||||
"""Stage the two tracked documents and one derivative model artifact."""
|
||||
sources = {
|
||||
"README.md": CARD_DIR / "README.md",
|
||||
"metrics.json": CARD_DIR / "metrics.json",
|
||||
MODEL_FILE: src.expanduser().resolve() / MODEL_FILE,
|
||||
}
|
||||
missing = [str(path) for path in sources.values() if not path.is_file()]
|
||||
if missing:
|
||||
raise SystemExit(f"missing publication files: {', '.join(missing)}")
|
||||
dest.mkdir(parents=True, exist_ok=True)
|
||||
for name, path in sources.items():
|
||||
shutil.copy2(path, dest / name)
|
||||
|
||||
|
||||
def publish(stage: Path, *, token: str, message: str) -> str:
|
||||
"""Upload the exact staged allowlist and return the Hub revision."""
|
||||
return publish_model_folder(
|
||||
stage,
|
||||
repo_id=HUB_REPO,
|
||||
token=token,
|
||||
message=message,
|
||||
allow_patterns=PUBLIC_FILES,
|
||||
)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--src", type=Path, required=True)
|
||||
parser.add_argument("--message", default="Publish source-pipeline classifier")
|
||||
args = parser.parse_args()
|
||||
logging.basicConfig(level=logging.INFO, format="%(message)s")
|
||||
token = os.environ.get("HF_TOKEN", "").strip()
|
||||
if not token:
|
||||
raise SystemExit("HF_TOKEN is not set (write role required)")
|
||||
with tempfile.TemporaryDirectory(prefix="raiw-source-hf-") as tmp:
|
||||
stage = Path(tmp)
|
||||
stage_release(stage, args.src)
|
||||
revision = publish(stage, token=token, message=args.message)
|
||||
log.info("published %s revision=%s", HUB_REPO, revision or "(unknown)")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -30,6 +30,14 @@ import numpy as np
|
||||
from numpy.typing import NDArray
|
||||
from PIL import Image, ImageFilter
|
||||
|
||||
from remove_ai_watermarks._internal import source_spectral as _spectral
|
||||
|
||||
SpectralGeometry = _spectral.SpectralGeometry
|
||||
make_geometry = _spectral.make_geometry
|
||||
phase_free_log_spectrum = _spectral.phase_free_log_spectrum
|
||||
spectral_feature = _spectral.spectral_feature
|
||||
_source_feature_from_image = _spectral.feature_from_image
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
IMAGE_SUFFIXES = frozenset({".jpg", ".jpeg", ".png", ".webp"})
|
||||
@@ -40,17 +48,6 @@ IntArray = NDArray[np.int64]
|
||||
BoolArray = NDArray[np.bool_]
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SpectralGeometry:
|
||||
"""Precomputed coordinates for one phase-free spectral representation."""
|
||||
|
||||
size: int
|
||||
grid: int
|
||||
radius: NDArray[np.int32]
|
||||
radius_counts: IntArray
|
||||
passband: BoolArray
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class FoldModel:
|
||||
"""One standardized dual-ridge model and its held-out rows."""
|
||||
@@ -62,51 +59,6 @@ class FoldModel:
|
||||
weights: FloatArray
|
||||
|
||||
|
||||
def make_geometry(size: int = 256, grid: int = 32) -> SpectralGeometry:
|
||||
"""Build the radial detrending and pooling geometry."""
|
||||
if size < 16 or grid < 2 or size % grid:
|
||||
raise ValueError("size must be at least 16 and divisible by grid")
|
||||
yy, xx = np.mgrid[:size, :size]
|
||||
radius = np.rint(np.hypot(yy - size // 2, xx - size // 2)).astype(np.int32)
|
||||
radius_counts = np.bincount(radius.ravel())
|
||||
passband = (radius >= 4) & (radius <= size * 0.48)
|
||||
return SpectralGeometry(size, grid, radius, radius_counts, passband)
|
||||
|
||||
|
||||
def phase_free_log_spectrum(channel: FloatArray) -> FloatArray:
|
||||
"""Return centered log FFT magnitude, deliberately discarding phase."""
|
||||
if channel.ndim != 2:
|
||||
raise ValueError("channel must be a two-dimensional array")
|
||||
centered = np.asarray(channel, dtype=np.float64) - float(np.mean(channel))
|
||||
return np.log1p(np.abs(np.fft.fftshift(np.fft.fft2(centered))))
|
||||
|
||||
|
||||
def spectral_feature(pixels: FloatArray, geometry: SpectralGeometry) -> FloatArray:
|
||||
"""Extract pooled radial-residual spectra from luma and opponent color."""
|
||||
if pixels.shape != (geometry.size, geometry.size, 3):
|
||||
raise ValueError(f"pixels must have shape {(geometry.size, geometry.size, 3)}, got {pixels.shape}")
|
||||
red, green, blue = np.moveaxis(np.asarray(pixels, dtype=np.float64), 2, 0)
|
||||
channels = (
|
||||
0.299 * red + 0.587 * green + 0.114 * blue,
|
||||
red - green,
|
||||
blue - (red + green) / 2.0,
|
||||
)
|
||||
block = geometry.size // geometry.grid
|
||||
features: list[FloatArray] = []
|
||||
for channel in channels:
|
||||
spectrum = phase_free_log_spectrum(channel)
|
||||
radial_sum = np.bincount(geometry.radius.ravel(), weights=spectrum.ravel())
|
||||
radial_mean = radial_sum / geometry.radius_counts
|
||||
residual = np.where(
|
||||
geometry.passband,
|
||||
spectrum - radial_mean[geometry.radius],
|
||||
0.0,
|
||||
)
|
||||
pooled = residual.reshape(geometry.grid, block, geometry.grid, block).mean(axis=(1, 3))
|
||||
features.append(pooled.ravel())
|
||||
return np.concatenate(features)
|
||||
|
||||
|
||||
def _apply_attack(image: Image.Image, attack: str) -> Image.Image:
|
||||
"""Apply one declared robustness transform before canonical resizing."""
|
||||
if attack == "original":
|
||||
@@ -140,9 +92,7 @@ def feature_from_path(path: Path, geometry: SpectralGeometry, attack: str) -> Fl
|
||||
def feature_from_image(image: Image.Image, geometry: SpectralGeometry, attack: str) -> FloatArray:
|
||||
"""Extract one attacked feature from an already decoded RGB image."""
|
||||
attacked = _apply_attack(image, attack)
|
||||
canonical = attacked.resize((geometry.size, geometry.size), Image.Resampling.LANCZOS)
|
||||
pixels = np.asarray(canonical, dtype=np.float64) / 255.0
|
||||
return spectral_feature(pixels, geometry)
|
||||
return _source_feature_from_image(attacked, geometry)
|
||||
|
||||
|
||||
def discover_grouped_paths(
|
||||
|
||||
@@ -14,6 +14,7 @@ High-level API (lazy, so ``import remove_ai_watermarks`` stays cheap)::
|
||||
raiw.remove_video_metadata("in.mp4", "out.mp4") # verified metadata strip
|
||||
raiw.remove_video_invisible("in.mp4", "out.mp4") # calibrated video-pixel regeneration
|
||||
raiw.remove_video_visible("in.mp4", "out.mp4") # stable visible video-mark removal
|
||||
raiw.classify_source("in.png") # source pipeline, not SynthID detection
|
||||
|
||||
For a provenance verdict use the ``identify`` submodule::
|
||||
|
||||
@@ -33,7 +34,7 @@ _os.environ.setdefault("TRANSFORMERS_VERBOSITY", "error")
|
||||
_warnings.filterwarnings("ignore", message=r".*ImageProcessorFast.*")
|
||||
|
||||
|
||||
__version__ = "0.40.6"
|
||||
__version__ = "0.41.1"
|
||||
|
||||
__all__ = [
|
||||
"BatchItemResult",
|
||||
@@ -41,8 +42,10 @@ __all__ = [
|
||||
"InvisibleOptions",
|
||||
"MetadataStripIncomplete",
|
||||
"RemoveAllResult",
|
||||
"SourceClassification",
|
||||
"VisibleRemovalResult",
|
||||
"__version__",
|
||||
"classify_source",
|
||||
"identify_video",
|
||||
"inspect_video_metadata",
|
||||
"remove_all",
|
||||
@@ -71,6 +74,7 @@ if TYPE_CHECKING:
|
||||
remove_visible_detailed,
|
||||
visible_provenance,
|
||||
)
|
||||
from remove_ai_watermarks.source_classify import SourceClassification, classify_source
|
||||
from remove_ai_watermarks.video import (
|
||||
identify_video,
|
||||
inspect_video_metadata,
|
||||
@@ -101,6 +105,10 @@ def __getattr__(name: str) -> object:
|
||||
from remove_ai_watermarks import api
|
||||
|
||||
return getattr(api, name)
|
||||
if name in ("SourceClassification", "classify_source"):
|
||||
from remove_ai_watermarks import source_classify
|
||||
|
||||
return getattr(source_classify, name)
|
||||
if name in (
|
||||
"identify_video",
|
||||
"inspect_video_metadata",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Generated C2PA soft-binding registry; do not edit by hand.
|
||||
|
||||
Source: https://raw.githubusercontent.com/c2pa-org/softbinding-algorithm-list/main/softbinding-algorithm-list.json
|
||||
Revision: c0218cd628c9163ad62a08c3deff5a10fa0e0d0b
|
||||
Revision: 1b3ef637890ea8228bca13d8941cfd54da941867
|
||||
Source license: CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/)
|
||||
Changes: validated, ordered, and reduced to fields used at runtime; descriptions
|
||||
are shortened to their first sentence for display.
|
||||
@@ -12,7 +12,7 @@ from __future__ import annotations
|
||||
C2PA_SOFT_BINDING_SOURCE_URL = (
|
||||
"https://raw.githubusercontent.com/c2pa-org/softbinding-algorithm-list/main/softbinding-algorithm-list.json"
|
||||
)
|
||||
C2PA_SOFT_BINDING_SOURCE_REVISION = "c0218cd628c9163ad62a08c3deff5a10fa0e0d0b"
|
||||
C2PA_SOFT_BINDING_SOURCE_REVISION = "1b3ef637890ea8228bca13d8941cfd54da941867"
|
||||
C2PA_SOFT_BINDING_SOURCE_LICENSE = "CC BY 4.0"
|
||||
|
||||
C2PA_SOFT_BINDING_ROWS: tuple[
|
||||
@@ -387,7 +387,7 @@ C2PA_SOFT_BINDING_ROWS: tuple[
|
||||
"eu.sasha.softbinding.v1",
|
||||
"watermark",
|
||||
("image",),
|
||||
("image/jpeg", "image/png"),
|
||||
("image/jpeg", "image/png", "image/webp"),
|
||||
(
|
||||
"Invisible watermarking technology for image and video assets, enabling C2PA manifest "
|
||||
"recovery and content provenance verification after metadata stripping or transcoding"
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
"""Phase-free spectral features for source/export-pipeline classification."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from functools import lru_cache
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from numpy.typing import NDArray
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SpectralGeometry:
|
||||
"""Precomputed coordinates for one phase-free spectral representation."""
|
||||
|
||||
size: int
|
||||
grid: int
|
||||
radius: NDArray[np.int32]
|
||||
radius_counts: NDArray[np.int64]
|
||||
passband: NDArray[np.bool_]
|
||||
|
||||
|
||||
@lru_cache(maxsize=4)
|
||||
def make_geometry(size: int = 256, grid: int = 16) -> SpectralGeometry:
|
||||
"""Build radial detrending and pooling geometry."""
|
||||
if size < 16 or grid < 2 or size % grid:
|
||||
raise ValueError("size must be at least 16 and divisible by grid")
|
||||
yy, xx = np.mgrid[:size, :size]
|
||||
radius = np.rint(np.hypot(yy - size // 2, xx - size // 2)).astype(np.int32)
|
||||
radius_counts = np.bincount(radius.ravel())
|
||||
passband = (radius >= 4) & (radius <= size * 0.48)
|
||||
return SpectralGeometry(size, grid, radius, radius_counts, passband)
|
||||
|
||||
|
||||
def phase_free_log_spectrum(channel: NDArray[np.float64]) -> NDArray[np.float64]:
|
||||
"""Return centered log FFT magnitude, deliberately discarding phase."""
|
||||
if channel.ndim != 2:
|
||||
raise ValueError("channel must be a two-dimensional array")
|
||||
centered = channel - float(np.mean(channel))
|
||||
return np.log1p(np.abs(np.fft.fftshift(np.fft.fft2(centered))))
|
||||
|
||||
|
||||
def spectral_feature(pixels: NDArray[np.float64], geometry: SpectralGeometry) -> NDArray[np.float64]:
|
||||
"""Extract pooled radial-residual spectra from luma and opponent color."""
|
||||
expected = (geometry.size, geometry.size, 3)
|
||||
if pixels.shape != expected:
|
||||
raise ValueError(f"pixels must have shape {expected}, got {pixels.shape}")
|
||||
red, green, blue = np.moveaxis(pixels, 2, 0)
|
||||
channels = (
|
||||
0.299 * red + 0.587 * green + 0.114 * blue,
|
||||
red - green,
|
||||
blue - (red + green) / 2.0,
|
||||
)
|
||||
block = geometry.size // geometry.grid
|
||||
features: list[NDArray[np.float64]] = []
|
||||
for channel in channels:
|
||||
spectrum = phase_free_log_spectrum(channel)
|
||||
radial_sum = np.bincount(geometry.radius.ravel(), weights=spectrum.ravel())
|
||||
radial_mean = radial_sum / geometry.radius_counts
|
||||
residual = np.where(
|
||||
geometry.passband,
|
||||
spectrum - radial_mean[geometry.radius],
|
||||
0.0,
|
||||
)
|
||||
pooled = residual.reshape(geometry.grid, block, geometry.grid, block).mean(axis=(1, 3))
|
||||
features.append(pooled.ravel())
|
||||
return np.concatenate(features)
|
||||
|
||||
|
||||
def feature_from_image(image: Image.Image, geometry: SpectralGeometry) -> NDArray[np.float64]:
|
||||
"""Resize an RGB image canonically and extract its phase-free feature."""
|
||||
canonical = image.resize((geometry.size, geometry.size), Image.Resampling.LANCZOS)
|
||||
pixels = np.asarray(canonical, dtype=np.float64) / 255.0
|
||||
return spectral_feature(pixels, geometry)
|
||||
@@ -0,0 +1,238 @@
|
||||
"""Abstaining source/export-pipeline classification from image pixels.
|
||||
|
||||
This module does not detect or decode SynthID. It labels original-export-looking
|
||||
pixel pipelines as OpenAI, Google, or unknown after metadata may have been
|
||||
removed. Call it explicitly; ``identify`` never imports or runs it.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
import os
|
||||
from dataclasses import dataclass
|
||||
from functools import lru_cache
|
||||
from hashlib import sha256
|
||||
from importlib import import_module
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Any, Literal
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from numpy.typing import NDArray
|
||||
|
||||
SOURCE_CLASSIFY_EXTRA = "'remove-ai-watermarks[source-classify]'"
|
||||
WEIGHTS_ENV = "RAIW_SOURCE_CLASSIFY_WEIGHTS"
|
||||
WEIGHTS_REPO = "wiltodelta/raiw-source-classify"
|
||||
WEIGHTS_REVISION = "4b8b408d03dac20231d46217977fd164414b6b9e"
|
||||
MODEL_FILE = "source-pipeline-mlp.npz"
|
||||
MODEL_SHA256 = "6430b0738869ff07b6845b7c6149c2a83bc3f3785878082886c241c6f7ca0c4a"
|
||||
CLASSES = ("openai", "google", "unknown")
|
||||
PROVIDERS = CLASSES[:2]
|
||||
FEATURE_WIDTH = 892
|
||||
SPECTRAL_GRID = 16
|
||||
CLAIM = "source/export pipeline classification; not SynthID detection"
|
||||
# NumPy and the training runtime can differ by a few float32 ULPs at a matrix
|
||||
# boundary. Treat values within this tolerance as the stored threshold so the
|
||||
# exported runtime preserves the training runtime's >= decision.
|
||||
THRESHOLD_TOLERANCE = 1e-5
|
||||
|
||||
SourceLabel = Literal["openai", "google", "unknown"]
|
||||
SourceReason = Literal["classified", "abstained", "feature_unavailable"]
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SourceClassification:
|
||||
"""One narrow source-pipeline result with an explicit abstention."""
|
||||
|
||||
label: SourceLabel
|
||||
reason: SourceReason
|
||||
scores: dict[str, float]
|
||||
margins: dict[str, float]
|
||||
thresholds: dict[str, float]
|
||||
claim: str = CLAIM
|
||||
model_revision: str = WEIGHTS_REVISION
|
||||
|
||||
def to_dict(self) -> dict[str, Any]:
|
||||
return {
|
||||
"label": self.label,
|
||||
"reason": self.reason,
|
||||
"scores": self.scores,
|
||||
"margins": self.margins,
|
||||
"thresholds": self.thresholds,
|
||||
"claim": self.claim,
|
||||
"model_revision": self.model_revision,
|
||||
}
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class _Model:
|
||||
mean: NDArray[Any]
|
||||
scale: NDArray[Any]
|
||||
margins: NDArray[Any]
|
||||
layer0_weight: NDArray[Any]
|
||||
layer0_bias: NDArray[Any]
|
||||
layer1_weight: NDArray[Any]
|
||||
layer1_bias: NDArray[Any]
|
||||
output_weight: NDArray[Any]
|
||||
output_bias: NDArray[Any]
|
||||
|
||||
|
||||
def is_available() -> bool:
|
||||
"""Return whether the lightweight source-classify runtime is installed."""
|
||||
from remove_ai_watermarks.optional_deps import module_available
|
||||
|
||||
return module_available("numpy", "cv2", "huggingface_hub")
|
||||
|
||||
|
||||
def _model_path() -> Path:
|
||||
override = os.environ.get(WEIGHTS_ENV, "").strip()
|
||||
if override:
|
||||
path = Path(override).expanduser()
|
||||
candidate = path / MODEL_FILE if path.is_dir() else path
|
||||
if not candidate.is_file():
|
||||
raise RuntimeError(f"{WEIGHTS_ENV} does not contain {MODEL_FILE}: {path}")
|
||||
return candidate
|
||||
try:
|
||||
hub: Any = import_module("huggingface_hub")
|
||||
except ModuleNotFoundError as error:
|
||||
raise RuntimeError(f"Install {SOURCE_CLASSIFY_EXTRA} to download the source classifier") from error
|
||||
return Path(
|
||||
hub.hf_hub_download(
|
||||
repo_id=WEIGHTS_REPO,
|
||||
filename=MODEL_FILE,
|
||||
revision=WEIGHTS_REVISION,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@lru_cache(maxsize=2)
|
||||
def _load_model(path: Path, expected_sha256: str | None = MODEL_SHA256) -> _Model:
|
||||
import numpy as np
|
||||
|
||||
if expected_sha256 is not None:
|
||||
digest = sha256(path.read_bytes()).hexdigest()
|
||||
if digest != expected_sha256:
|
||||
raise ValueError(f"model SHA-256 mismatch: expected {expected_sha256}, got {digest}")
|
||||
required = {
|
||||
"classes",
|
||||
"kind",
|
||||
"mean",
|
||||
"scale",
|
||||
"margins",
|
||||
"layer0_weight",
|
||||
"layer0_bias",
|
||||
"layer1_weight",
|
||||
"layer1_bias",
|
||||
"output_weight",
|
||||
"output_bias",
|
||||
"activation",
|
||||
}
|
||||
with np.load(path, allow_pickle=False) as payload:
|
||||
if set(payload.files) != required:
|
||||
raise ValueError(f"model fields must be exactly {sorted(required)}")
|
||||
if tuple(str(value) for value in payload["classes"]) != CLASSES:
|
||||
raise ValueError(f"model classes must be {CLASSES}")
|
||||
if str(payload["kind"]) != "fused":
|
||||
raise ValueError("model kind must be fused")
|
||||
if str(payload["activation"]) != "gelu_exact":
|
||||
raise ValueError("model activation must be gelu_exact")
|
||||
arrays = {
|
||||
name: np.asarray(
|
||||
payload[name],
|
||||
dtype=np.float64 if name in {"mean", "scale", "margins"} else np.float32,
|
||||
)
|
||||
for name in required - {"classes", "kind", "activation"}
|
||||
}
|
||||
shapes = {
|
||||
"mean": (FEATURE_WIDTH,),
|
||||
"scale": (FEATURE_WIDTH,),
|
||||
"margins": (len(PROVIDERS),),
|
||||
"layer0_weight": (128, FEATURE_WIDTH),
|
||||
"layer0_bias": (128,),
|
||||
"layer1_weight": (64, 128),
|
||||
"layer1_bias": (64,),
|
||||
"output_weight": (len(CLASSES), 64),
|
||||
"output_bias": (len(CLASSES),),
|
||||
}
|
||||
for name, expected in shapes.items():
|
||||
if arrays[name].shape != expected:
|
||||
raise ValueError(f"model field {name} must have shape {expected}")
|
||||
if not np.all(np.isfinite(arrays[name])):
|
||||
raise ValueError(f"model field {name} must be finite")
|
||||
if np.any(arrays["scale"] <= 0) or np.any(arrays["margins"] < 0):
|
||||
raise ValueError("model scales must be positive and margins non-negative")
|
||||
return _Model(**arrays)
|
||||
|
||||
|
||||
def _gelu_exact(values: NDArray[Any]) -> NDArray[Any]:
|
||||
import numpy as np
|
||||
|
||||
flat = values.ravel()
|
||||
errors = np.fromiter((math.erf(float(value) / math.sqrt(2.0)) for value in flat), dtype=np.float32)
|
||||
return (0.5 * flat * (1.0 + errors)).reshape(values.shape).astype(np.float32)
|
||||
|
||||
|
||||
def _scores(features: NDArray[Any], model: _Model) -> NDArray[Any]:
|
||||
import numpy as np
|
||||
|
||||
values = np.asarray((features - model.mean) / model.scale, dtype=np.float32)
|
||||
hidden0 = _gelu_exact(model.layer0_weight @ values + model.layer0_bias)
|
||||
hidden1 = _gelu_exact(model.layer1_weight @ hidden0 + model.layer1_bias)
|
||||
return np.asarray(model.output_weight @ hidden1 + model.output_bias, dtype=np.float64)
|
||||
|
||||
|
||||
def _result(raw_scores: NDArray[Any], thresholds: NDArray[Any]) -> SourceClassification:
|
||||
import numpy as np
|
||||
|
||||
margins = np.empty(len(PROVIDERS), dtype=np.float64)
|
||||
for index in range(len(PROVIDERS)):
|
||||
competitors = np.delete(raw_scores, index)
|
||||
margins[index] = raw_scores[index] - float(np.max(competitors))
|
||||
excess = margins - thresholds
|
||||
best = int(np.argmax(excess))
|
||||
accepted = bool(excess[best] >= -THRESHOLD_TOLERANCE)
|
||||
label: SourceLabel = PROVIDERS[best] if accepted else "unknown"
|
||||
return SourceClassification(
|
||||
label=label,
|
||||
reason="classified" if accepted else "abstained",
|
||||
scores={name: float(raw_scores[index]) for index, name in enumerate(CLASSES)},
|
||||
margins={name: float(margins[index]) for index, name in enumerate(PROVIDERS)},
|
||||
thresholds={name: float(thresholds[index]) for index, name in enumerate(PROVIDERS)},
|
||||
)
|
||||
|
||||
|
||||
def _unavailable(model: _Model) -> SourceClassification:
|
||||
return SourceClassification(
|
||||
label="unknown",
|
||||
reason="feature_unavailable",
|
||||
scores={},
|
||||
margins={},
|
||||
thresholds={name: float(model.margins[index]) for index, name in enumerate(PROVIDERS)},
|
||||
)
|
||||
|
||||
|
||||
def classify_source(source: str | os.PathLike[str]) -> SourceClassification:
|
||||
"""Classify an original-looking image export, abstaining as ``unknown``.
|
||||
|
||||
The result describes a complete source/export pipeline. It is not evidence
|
||||
that SynthID is present or absent.
|
||||
"""
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
|
||||
from remove_ai_watermarks import image_io
|
||||
from remove_ai_watermarks._internal.forensic_124d import image_features
|
||||
from remove_ai_watermarks._internal.source_spectral import feature_from_image, make_geometry
|
||||
|
||||
model = _load_model(_model_path(), expected_sha256=MODEL_SHA256)
|
||||
image_io._register_heif() # pyright: ignore[reportPrivateUsage]
|
||||
with Image.open(source) as opened:
|
||||
image = opened.convert("RGB")
|
||||
pixels = np.asarray(image, dtype=np.uint8)
|
||||
forensic = image_features(pixels)
|
||||
if forensic is None:
|
||||
return _unavailable(model)
|
||||
spectral = feature_from_image(image, make_geometry(grid=SPECTRAL_GRID))
|
||||
features = np.concatenate((forensic, spectral))
|
||||
if features.shape != (FEATURE_WIDTH,) or not np.all(np.isfinite(features)):
|
||||
return _unavailable(model)
|
||||
return _result(_scores(features, model), model.margins)
|
||||
@@ -23,6 +23,10 @@ class TestTopLevelExports:
|
||||
assert raiw.remove_visible_detailed is api.remove_visible_detailed
|
||||
assert raiw.VisibleRemovalResult is api.VisibleRemovalResult
|
||||
assert raiw.visible_provenance is api.visible_provenance
|
||||
from remove_ai_watermarks import source_classify
|
||||
|
||||
assert raiw.classify_source is source_classify.classify_source
|
||||
assert raiw.SourceClassification is source_classify.SourceClassification
|
||||
|
||||
def test_unknown_attribute_raises(self):
|
||||
with pytest.raises(AttributeError):
|
||||
|
||||
@@ -2,29 +2,21 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib.util
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
def _publish() -> Any:
|
||||
path = Path(__file__).resolve().parents[1] / "scripts" / "publish_photo_classify_hf.py"
|
||||
spec = importlib.util.spec_from_file_location("publish_photo_classify_hf", path)
|
||||
assert spec is not None
|
||||
assert spec.loader is not None
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
SCRIPTS = Path(__file__).resolve().parents[1] / "scripts"
|
||||
sys.path.insert(0, str(SCRIPTS))
|
||||
import publish_photo_classify_hf as publish # noqa: E402
|
||||
|
||||
|
||||
def test_hub_id_is_the_photo_classify_repo() -> None:
|
||||
assert _publish().HUB_REPO == "wiltodelta/raiw-photo-classify"
|
||||
assert publish.HUB_REPO == "wiltodelta/raiw-photo-classify"
|
||||
|
||||
|
||||
def test_publish_freeze_includes_both_clip_runtimes() -> None:
|
||||
publish = _publish()
|
||||
from remove_ai_watermarks.classify import _WEIGHT_FILES, CLIP_ONNX_FILE, WEIGHTS_ALLOW_PATTERNS
|
||||
|
||||
assert publish.CLIP_FILE in publish.WEIGHT_FILES
|
||||
@@ -41,7 +33,7 @@ def test_weight_paths_accepts_run1_layout(tmp_path: Path) -> None:
|
||||
run1.mkdir()
|
||||
(run1 / "detector.pt").write_bytes(b"det")
|
||||
(run1 / "provider.pt").write_bytes(b"prov")
|
||||
found = _publish().weight_paths(tmp_path)
|
||||
found = publish.weight_paths(tmp_path)
|
||||
assert found["detector.pt"] == run1 / "detector.pt"
|
||||
assert found["clip-l-ft.pt"] == tmp_path / "clip-l-ft.pt"
|
||||
|
||||
@@ -50,12 +42,12 @@ def test_weight_paths_requires_every_file(tmp_path: Path) -> None:
|
||||
(tmp_path / "clip-l-ft.pt").write_bytes(b"clip")
|
||||
(tmp_path / "clip-l-ft-vision-fp32.onnx").write_bytes(b"onnx")
|
||||
with pytest.raises(SystemExit, match=r"missing probe-weights-clip-l-ft\.npz"):
|
||||
_publish().weight_paths(tmp_path)
|
||||
publish.weight_paths(tmp_path)
|
||||
|
||||
|
||||
def test_stage_card_copies_tracked_files(tmp_path: Path) -> None:
|
||||
dest = tmp_path / "stage"
|
||||
_publish().stage_card(dest)
|
||||
publish.stage_card(dest)
|
||||
assert (dest / "README.md").is_file()
|
||||
assert (dest / "operating-point.json").is_file()
|
||||
text = (dest / "operating-point.json").read_text()
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
"""Local staging checks for the source-classifier Hub publisher."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
SCRIPTS = Path(__file__).resolve().parents[1] / "scripts"
|
||||
sys.path.insert(0, str(SCRIPTS))
|
||||
import publish_source_classify_hf as publish # noqa: E402
|
||||
|
||||
|
||||
def test_hub_id_is_the_source_classify_repo() -> None:
|
||||
assert publish.HUB_REPO == "wiltodelta/raiw-source-classify"
|
||||
|
||||
|
||||
def test_stage_release_copies_only_public_artifacts(tmp_path: Path) -> None:
|
||||
source = tmp_path / "source"
|
||||
source.mkdir()
|
||||
(source / "source-pipeline-mlp.npz").write_bytes(b"weights")
|
||||
destination = tmp_path / "stage"
|
||||
publish.stage_release(destination, source)
|
||||
assert sorted(path.name for path in destination.iterdir()) == sorted(publish.PUBLIC_FILES)
|
||||
|
||||
|
||||
def test_stage_release_requires_the_model(tmp_path: Path) -> None:
|
||||
with pytest.raises(SystemExit, match=r"source-pipeline-mlp\.npz"):
|
||||
publish.stage_release(tmp_path / "stage", tmp_path)
|
||||
|
||||
|
||||
def test_card_sha_matches_the_metrics_manifest() -> None:
|
||||
metrics = json.loads((publish.CARD_DIR / "metrics.json").read_text())
|
||||
card = (publish.CARD_DIR / "README.md").read_text()
|
||||
assert metrics["artifact_sha256"] in card
|
||||
@@ -0,0 +1,123 @@
|
||||
"""Unit contracts for the lightweight source-pipeline runtime."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from hashlib import sha256
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from remove_ai_watermarks import source_classify
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def test_model_identity_is_immutable() -> None:
|
||||
assert re.fullmatch(r"[0-9a-f]{40}", source_classify.WEIGHTS_REVISION)
|
||||
assert re.fullmatch(r"[0-9a-f]{64}", source_classify.MODEL_SHA256)
|
||||
|
||||
|
||||
def _artifact(path: Path, *, activation: str = "gelu_exact", output_bias: tuple[float, ...] = (0.0, 0.0, 0.0)) -> None:
|
||||
np.savez_compressed(
|
||||
path,
|
||||
classes=np.asarray(source_classify.CLASSES),
|
||||
kind=np.asarray("fused"),
|
||||
mean=np.zeros(source_classify.FEATURE_WIDTH),
|
||||
scale=np.ones(source_classify.FEATURE_WIDTH),
|
||||
margins=np.asarray((0.5, 0.5)),
|
||||
layer0_weight=np.zeros((128, source_classify.FEATURE_WIDTH), dtype=np.float32),
|
||||
layer0_bias=np.zeros(128, dtype=np.float32),
|
||||
layer1_weight=np.zeros((64, 128), dtype=np.float32),
|
||||
layer1_bias=np.zeros(64, dtype=np.float32),
|
||||
output_weight=np.zeros((3, 64), dtype=np.float32),
|
||||
output_bias=np.asarray(output_bias, dtype=np.float32),
|
||||
activation=np.asarray(activation),
|
||||
)
|
||||
|
||||
|
||||
def test_loader_accepts_the_exact_pickle_free_schema(tmp_path: Path) -> None:
|
||||
path = tmp_path / source_classify.MODEL_FILE
|
||||
_artifact(path)
|
||||
|
||||
model = source_classify._load_model(path, expected_sha256=None)
|
||||
|
||||
assert model.mean.shape == (source_classify.FEATURE_WIDTH,)
|
||||
assert model.margins.tolist() == pytest.approx([0.5, 0.5])
|
||||
|
||||
|
||||
def test_loader_rejects_the_mislabeled_activation(tmp_path: Path) -> None:
|
||||
path = tmp_path / source_classify.MODEL_FILE
|
||||
_artifact(path, activation="gelu_tanh")
|
||||
|
||||
with pytest.raises(ValueError, match="activation must be gelu_exact"):
|
||||
source_classify._load_model(path, expected_sha256=None)
|
||||
|
||||
|
||||
def test_provider_threshold_mutation_changes_the_real_decision(tmp_path: Path) -> None:
|
||||
path = tmp_path / source_classify.MODEL_FILE
|
||||
_artifact(path, output_bias=(2.0, 0.0, 0.0))
|
||||
model = source_classify._load_model(path, expected_sha256=None)
|
||||
features = np.zeros(source_classify.FEATURE_WIDTH, dtype=np.float32)
|
||||
scores = source_classify._scores(features, model)
|
||||
|
||||
accepted = source_classify._result(scores, np.asarray((1.5, 0.5), dtype=np.float32))
|
||||
rejected = source_classify._result(scores, np.asarray((2.5, 0.5), dtype=np.float32))
|
||||
|
||||
assert accepted.label == "openai"
|
||||
assert accepted.reason == "classified"
|
||||
assert rejected.label == "unknown"
|
||||
assert rejected.reason == "abstained"
|
||||
|
||||
|
||||
def test_a_float32_threshold_tie_preserves_the_inclusive_training_decision() -> None:
|
||||
scores = np.asarray((2.0, 0.0, 0.0), dtype=np.float32)
|
||||
result = source_classify._result(scores, np.asarray((2.0, 0.5), dtype=np.float32))
|
||||
|
||||
assert result.label == "openai"
|
||||
assert result.reason == "classified"
|
||||
|
||||
|
||||
def test_small_image_returns_honest_unknown(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
from PIL import Image
|
||||
|
||||
weights = tmp_path / "weights"
|
||||
weights.mkdir()
|
||||
_artifact(weights / source_classify.MODEL_FILE)
|
||||
monkeypatch.setenv(source_classify.WEIGHTS_ENV, str(weights))
|
||||
monkeypatch.setattr(
|
||||
source_classify,
|
||||
"MODEL_SHA256",
|
||||
sha256((weights / source_classify.MODEL_FILE).read_bytes()).hexdigest(),
|
||||
)
|
||||
image = tmp_path / "small.png"
|
||||
Image.new("RGB", (128, 128), "white").save(image)
|
||||
|
||||
result = source_classify.classify_source(image)
|
||||
|
||||
assert result.label == "unknown"
|
||||
assert result.reason == "feature_unavailable"
|
||||
assert result.scores == {}
|
||||
|
||||
|
||||
def test_model_hash_is_a_runtime_precondition(tmp_path: Path) -> None:
|
||||
path = tmp_path / source_classify.MODEL_FILE
|
||||
_artifact(path)
|
||||
|
||||
with pytest.raises(ValueError, match="model SHA-256 mismatch"):
|
||||
source_classify._load_model(path, expected_sha256="0" * 64)
|
||||
|
||||
|
||||
def test_spectral_feature_is_finite_and_has_the_frozen_width() -> None:
|
||||
from PIL import Image
|
||||
|
||||
from remove_ai_watermarks._internal.source_spectral import feature_from_image, make_geometry
|
||||
|
||||
rng = np.random.default_rng(20260916)
|
||||
pixels = rng.integers(0, 256, size=(300, 400, 3), dtype=np.uint8)
|
||||
feature = feature_from_image(Image.fromarray(pixels), make_geometry())
|
||||
|
||||
assert feature.shape == (768,)
|
||||
assert np.all(np.isfinite(feature))
|
||||
@@ -26,7 +26,7 @@ resolution-markers = [
|
||||
|
||||
[[package]]
|
||||
name = "accelerate"
|
||||
version = "1.14.0"
|
||||
version = "1.15.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "huggingface-hub" },
|
||||
@@ -38,9 +38,9 @@ dependencies = [
|
||||
{ name = "safetensors" },
|
||||
{ name = "torch" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/8d/75/94cd5d389649578aca399e5aa822637eec18319a1dadc400ffe2f9a7493f/accelerate-1.14.0.tar.gz", hash = "sha256:41b9c4377a54e0b460a959b0defa1b736e4ca0a2373252d9a539964c2afe3c8d", size = 412167, upload-time = "2026-06-11T13:45:52.326Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f5/b5/1d3ed029ac71d3f2961346829a268da923698e9fd63f218f78841f216bfd/accelerate-1.15.0.tar.gz", hash = "sha256:5654f8c5eaa0d4fa68b33e287a97765da6849bf6d51dcac874e73fbbddfb6134", size = 422615, upload-time = "2026-09-09T13:04:49.078Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a8/db/253133d7e7cb40d3af384bb2f5c0b4a2b7fdcffbc95c688cc67a20a3c103/accelerate-1.14.0-py3-none-any.whl", hash = "sha256:e94390c2863b873be18f623f9df48a0d8fe5eff13ea7f1a00092b0a7904888c6", size = 389246, upload-time = "2026-06-11T13:45:50.477Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8a/4c/34f0450479d01195027260da68d8a3880683f1640c3ca5adf64acb3185f1/accelerate-1.15.0-py3-none-any.whl", hash = "sha256:97eacca0b73e45cb867dbf8c5d5d4dc32219544300e0c8992c7334dc2ef33cec", size = 394295, upload-time = "2026-09-09T13:04:47.331Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -800,7 +800,7 @@ name = "cuda-bindings"
|
||||
version = "13.3.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "cuda-pathfinder" },
|
||||
{ name = "cuda-pathfinder", marker = "sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'" },
|
||||
]
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/51/6b/457ca12dad3ee9bfcc9a545cfd6b64b359ba49de40f776f6e028e678f262/cuda_bindings-13.3.1-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c5879712accf6e14bb01aa5e67440eb84998b8d104b509cc7a6dc0b8f656a474", size = 6053539, upload-time = "2026-05-29T23:11:43.19Z" },
|
||||
@@ -833,43 +833,43 @@ wheels = [
|
||||
|
||||
[package.optional-dependencies]
|
||||
cublas = [
|
||||
{ name = "nvidia-cublas", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" },
|
||||
{ name = "nvidia-cuda-nvrtc", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" },
|
||||
{ name = "nvidia-cublas", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" },
|
||||
{ name = "nvidia-cuda-nvrtc", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" },
|
||||
]
|
||||
cudart = [
|
||||
{ name = "nvidia-cuda-runtime", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" },
|
||||
{ name = "nvidia-cuda-runtime", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" },
|
||||
]
|
||||
cufft = [
|
||||
{ name = "nvidia-cufft", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" },
|
||||
{ name = "nvidia-nvjitlink", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" },
|
||||
{ name = "nvidia-cufft", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" },
|
||||
{ name = "nvidia-nvjitlink", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" },
|
||||
]
|
||||
cufile = [
|
||||
{ name = "nvidia-cufile", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" },
|
||||
{ name = "nvidia-cufile", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" },
|
||||
]
|
||||
cupti = [
|
||||
{ name = "nvidia-cuda-cupti", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" },
|
||||
{ name = "nvidia-cuda-cupti", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" },
|
||||
]
|
||||
curand = [
|
||||
{ name = "nvidia-curand", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" },
|
||||
{ name = "nvidia-curand", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" },
|
||||
]
|
||||
cusolver = [
|
||||
{ name = "nvidia-cublas", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" },
|
||||
{ name = "nvidia-cusolver", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" },
|
||||
{ name = "nvidia-cusparse", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" },
|
||||
{ name = "nvidia-nvjitlink", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" },
|
||||
{ name = "nvidia-cublas", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" },
|
||||
{ name = "nvidia-cusolver", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" },
|
||||
{ name = "nvidia-cusparse", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" },
|
||||
{ name = "nvidia-nvjitlink", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" },
|
||||
]
|
||||
cusparse = [
|
||||
{ name = "nvidia-cusparse", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" },
|
||||
{ name = "nvidia-nvjitlink", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" },
|
||||
{ name = "nvidia-cusparse", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" },
|
||||
{ name = "nvidia-nvjitlink", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" },
|
||||
]
|
||||
nvjitlink = [
|
||||
{ name = "nvidia-nvjitlink", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" },
|
||||
{ name = "nvidia-nvjitlink", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" },
|
||||
]
|
||||
nvrtc = [
|
||||
{ name = "nvidia-cuda-nvrtc", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" },
|
||||
{ name = "nvidia-cuda-nvrtc", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" },
|
||||
]
|
||||
nvtx = [
|
||||
{ name = "nvidia-nvtx", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" },
|
||||
{ name = "nvidia-nvtx", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1066,7 +1066,7 @@ wheels = [
|
||||
|
||||
[package.optional-dependencies]
|
||||
http = [
|
||||
{ name = "aiohttp" },
|
||||
{ name = "aiohttp", marker = "python_full_version < '3.13'" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1198,8 +1198,8 @@ name = "httpcore2"
|
||||
version = "2.12.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "h11" },
|
||||
{ name = "truststore" },
|
||||
{ name = "h11", marker = "sys_platform != 'emscripten'" },
|
||||
{ name = "truststore", marker = "sys_platform != 'emscripten'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/be/ad/f4f0e57345f1870f3e8cb624e058d7eca6e5a27d33bcc3311d9b618734cd/httpcore2-2.12.0.tar.gz", hash = "sha256:9293522bba0aa7c4c8e9e3f040c16575bd8868e155a77fa30c7a9085a5eae648", size = 67548, upload-time = "2026-08-18T13:22:08.211Z" }
|
||||
wheels = [
|
||||
@@ -1249,7 +1249,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "huggingface-hub"
|
||||
version = "1.30.0"
|
||||
version = "1.31.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "click" },
|
||||
@@ -1262,9 +1262,9 @@ dependencies = [
|
||||
{ name = "tqdm" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/35/97/2eb4abaa5b969ed385066a0496a3823b3ff467fc1082e2202955f1867d60/huggingface_hub-1.30.0.tar.gz", hash = "sha256:e6a6120bc8c8e2723d03648434ee247088cceb55ba7067e7d34d692cad5fdb57", size = 964291, upload-time = "2026-09-03T10:05:14.053Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/9b/f0/61159db90b5cd275d55516fe27920828e7d3be4053fdbdb27c3f70e5f1ef/huggingface_hub-1.31.0.tar.gz", hash = "sha256:f8e9e710a210613fa5d0f26bba6da05ef4aef9fba5a0f23f508f5ac4d08b6f90", size = 968039, upload-time = "2026-09-10T10:27:22.724Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c3/0e/3e45bbe0dd48f4e56b1d46649d342de853cd1c7e815323472ab62687f153/huggingface_hub-1.30.0-py3-none-any.whl", hash = "sha256:96ae0a8e99a234374a6fe43e989ebd21c04640b91ab2927e7e5773ba1131ca59", size = 796795, upload-time = "2026-09-03T10:05:12.21Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c3/7f/3f886a625043b77312b80da2f2bf00b5ecbf5a73061af1aa0259cd258c9d/huggingface_hub-1.31.0-py3-none-any.whl", hash = "sha256:9dbb6a503cbe2494ea666695207e7262d410659e09134059deb83e5480864667", size = 798313, upload-time = "2026-09-10T10:27:20.798Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1461,15 +1461,15 @@ name = "lightning"
|
||||
version = "2.6.6"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "fsspec", extra = ["http"] },
|
||||
{ name = "lightning-utilities" },
|
||||
{ name = "packaging" },
|
||||
{ name = "pytorch-lightning" },
|
||||
{ name = "pyyaml" },
|
||||
{ name = "torch" },
|
||||
{ name = "torchmetrics" },
|
||||
{ name = "tqdm" },
|
||||
{ name = "typing-extensions" },
|
||||
{ name = "fsspec", extra = ["http"], marker = "python_full_version < '3.13'" },
|
||||
{ name = "lightning-utilities", marker = "python_full_version < '3.13'" },
|
||||
{ name = "packaging", marker = "python_full_version < '3.13'" },
|
||||
{ name = "pytorch-lightning", marker = "python_full_version < '3.13'" },
|
||||
{ name = "pyyaml", marker = "python_full_version < '3.13'" },
|
||||
{ name = "torch", marker = "python_full_version < '3.13'" },
|
||||
{ name = "torchmetrics", marker = "python_full_version < '3.13'" },
|
||||
{ name = "tqdm", marker = "python_full_version < '3.13'" },
|
||||
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/39/b8/fbf1469d917c3ed3b22e00ab3a852fb9a5f85b9dc7f7060eedc3449098e7/lightning-2.6.6.tar.gz", hash = "sha256:1c6329e241f8a85f29fedead89c1ee138b36caa08de60c260c6a7eaee81c8295", size = 659902, upload-time = "2026-09-10T09:41:03.009Z" }
|
||||
wheels = [
|
||||
@@ -1481,8 +1481,8 @@ name = "lightning-utilities"
|
||||
version = "0.15.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "packaging" },
|
||||
{ name = "typing-extensions" },
|
||||
{ name = "packaging", marker = "python_full_version < '3.13'" },
|
||||
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f1/45/7fa8f56b17dc0f0a41ec70dd307ecd6787254483549843bef4c30ab5adce/lightning_utilities-0.15.3.tar.gz", hash = "sha256:792ae0204c79f6859721ac7f386c237a33b0ed06ba775009cb894e010a842033", size = 33553, upload-time = "2026-02-22T14:48:53.348Z" }
|
||||
wheels = [
|
||||
@@ -1896,7 +1896,7 @@ name = "nvidia-cublas"
|
||||
version = "13.1.1.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "nvidia-cuda-nvrtc" },
|
||||
{ name = "nvidia-cuda-nvrtc", marker = "sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'" },
|
||||
]
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a7/a1/0bd24ee8c8d03adac032fd2909426a00c88f8c57961b1277ded97f91119f/nvidia_cublas-13.1.1.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:b7a210458267ac818974c53038fbec2e969d5c99f305ab15c72522fa9f001dd5", size = 542848918, upload-time = "2026-04-08T18:46:22.985Z" },
|
||||
@@ -1935,7 +1935,7 @@ name = "nvidia-cudnn-cu13"
|
||||
version = "9.24.0.43"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "nvidia-cublas" },
|
||||
{ name = "nvidia-cublas", marker = "sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'" },
|
||||
]
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ca/30/7c257e3d5cb4fecb147b93895c66e29c93f8e76d74b45bb418ff0587c4ec/nvidia_cudnn_cu13-9.24.0.43-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:a6812a554a1ff0413e9c52b84c26c050380649ab9615f9c16bded368ce9f421f", size = 650976863, upload-time = "2026-07-02T16:23:39.248Z" },
|
||||
@@ -1947,7 +1947,7 @@ name = "nvidia-cufft"
|
||||
version = "12.0.0.61"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "nvidia-nvjitlink" },
|
||||
{ name = "nvidia-nvjitlink", marker = "sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'" },
|
||||
]
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/8b/ae/f417a75c0259e85c1d2f83ca4e960289a5f814ed0cea74d18c353d3e989d/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2708c852ef8cd89d1d2068bdbece0aa188813a0c934db3779b9b1faa8442e5f5", size = 214053554, upload-time = "2025-09-04T08:31:38.196Z" },
|
||||
@@ -1977,9 +1977,9 @@ name = "nvidia-cusolver"
|
||||
version = "12.0.4.66"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "nvidia-cublas" },
|
||||
{ name = "nvidia-cusparse" },
|
||||
{ name = "nvidia-nvjitlink" },
|
||||
{ name = "nvidia-cublas", marker = "sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'" },
|
||||
{ name = "nvidia-cusparse", marker = "sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'" },
|
||||
{ name = "nvidia-nvjitlink", marker = "sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'" },
|
||||
]
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c8/c3/b30c9e935fc01e3da443ec0116ed1b2a009bb867f5324d3f2d7e533e776b/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:02c2457eaa9e39de20f880f4bd8820e6a1cfb9f9a34f820eb12a155aa5bc92d2", size = 223467760, upload-time = "2025-09-04T08:33:04.222Z" },
|
||||
@@ -1991,7 +1991,7 @@ name = "nvidia-cusparse"
|
||||
version = "12.6.3.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "nvidia-nvjitlink" },
|
||||
{ name = "nvidia-nvjitlink", marker = "sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'" },
|
||||
]
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/f8/94/5c26f33738ae35276672f12615a64bd008ed5be6d1ebcb23579285d960a9/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:80bcc4662f23f1054ee334a15c72b8940402975e0eab63178fc7e670aa59472c", size = 162155568, upload-time = "2025-09-04T08:33:42.864Z" },
|
||||
@@ -2058,7 +2058,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "onnxruntime"
|
||||
version = "1.29.0"
|
||||
version = "1.30.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "flatbuffers" },
|
||||
@@ -2068,35 +2068,35 @@ dependencies = [
|
||||
{ name = "protobuf" },
|
||||
]
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/7c/a8/0520890321b8ff40b908cf165a93eb58fbc8f85c14db637277ea866c9544/onnxruntime-1.29.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:07c5907474dec4a2792fd7626b753dc66707808385a6d9eecf993db0066a9d0f", size = 21420890, upload-time = "2026-08-17T22:53:33.429Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0b/77/8bd3e0008ff8d386305351109a7329ea57e51a3ab57bc92340f29c4a5b5d/onnxruntime-1.29.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:16925ef8497e2c07e4b5ae15b504079b3ab3f65e22c58efd10dde0f3caea969a", size = 20803602, upload-time = "2026-08-17T22:53:36.47Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3b/91/a66cd77f28379ede419672edda3184f1eb286db215dce1e7b976fae2d63b/onnxruntime-1.29.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:85f8e8406c52658735fe5c7fbfd3ebaa1ed340768324f6252e4274e374580a23", size = 23113193, upload-time = "2026-08-17T22:53:39.732Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1c/82/2da968405c42340f03de0bcdb63be09ae1004f820b2295590d48951b5cf2/onnxruntime-1.29.0-cp311-cp311-win_amd64.whl", hash = "sha256:0d4f427afac434b0070fe992b540ddf20a7aff2265f760f314d91331935b6b98", size = 13999253, upload-time = "2026-08-17T22:53:43.184Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/95/7a/70c9c893bf732ee66124c2d8de6a21fc9361ec62cf378f857043efcbf0eb/onnxruntime-1.29.0-cp311-cp311-win_arm64.whl", hash = "sha256:4eae472cf7dc3107dec1bb53cd6d142d1964616d08aae48654cd4254b2363c4b", size = 13741410, upload-time = "2026-08-17T22:53:45.521Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d4/80/381c1e9efed9cc32d00aa7cab0547dc84116cec906c3ffe3613686d6963a/onnxruntime-1.29.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:3a3814c041251d6a77fdf513fb282056538ee826d2f1178a0df3c549d3fff6ba", size = 21430049, upload-time = "2026-08-17T22:53:48.286Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/30/12/4be0e345d38fe707a701ca07e8f63c05b152a2e6285d1e43a7faf63fedd2/onnxruntime-1.29.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:d2fb19e848f7c33ed8d3182b52504aaa11c5e8da438bbb47296f85b133cbcf6b", size = 20816870, upload-time = "2026-08-17T22:53:51.169Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/96/eb/e6968f5e41aac3125f2ff5708855f09cb0b70d85ed3115b625b0b58305ba/onnxruntime-1.29.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:2b80d8c7ec2cc7438e4da3760b88c24568cba72c9ace96d668800a6c79419acb", size = 23136745, upload-time = "2026-08-17T22:53:53.92Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b4/80/5b28f1f1111210fc4a336ddbc6950f468ebf9a6a265420568f4f43fa33ce/onnxruntime-1.29.0-cp312-cp312-win_amd64.whl", hash = "sha256:4acf2b4948b7ede87221ca6332344b8facdc8059d6ac751a7d367d04532b02dd", size = 14001407, upload-time = "2026-08-17T22:53:56.486Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6f/d6/6883f89ea4b044e6e8447ebfaf9bcecdf457b7d80a683635e130b25498e0/onnxruntime-1.29.0-cp312-cp312-win_arm64.whl", hash = "sha256:dc61a79cb39afd66ab3f01fd2c23591a7f01de89c1668e1fb6315067fc279164", size = 13746981, upload-time = "2026-08-17T22:53:58.977Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/41/f8/d375facf60edaf41f5732f9f689c98a800fcc52df5cf6ddfb406703eb5a1/onnxruntime-1.29.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:be0f8ed688cfb1d4d5765a137193b7bfab0c8ea214eed99260b380bb525a3a7f", size = 21429708, upload-time = "2026-08-17T22:54:01.44Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c9/17/b9ad04051a8c4f504852ce0e8e10f9a6b2f1a331eedcdcc503df776dd0ea/onnxruntime-1.29.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:d67673c5367727860922c5262d724472f1b5539fb7ccf4c81a638f9b71719803", size = 20816263, upload-time = "2026-08-17T22:54:04.088Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/83/2c/d8eb945d2a372149df9705a8d5c8d7c6c46c987c5446dbcea9e1ea7f6556/onnxruntime-1.29.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:e2128f31f449e922c62dbe5d8b6b7b079f0bcaf2d56a102fa203cb6e5bb5ab19", size = 23136817, upload-time = "2026-08-17T22:54:06.714Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e1/3b/66b424c63fa92dfaa48d1719efaae66fc8c256b9426a832eda51d8dfe1e9/onnxruntime-1.29.0-cp313-cp313-win_amd64.whl", hash = "sha256:2945e1f82f81f27e88decea88c7861f45baea23818950d467bf3909aa303119e", size = 14001310, upload-time = "2026-08-17T22:54:09.13Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/83/22/d6a700e3a6322fa3d56fbe7cee9ffc53f35e77ffcd6b7e97f4b7722a27ab/onnxruntime-1.29.0-cp313-cp313-win_arm64.whl", hash = "sha256:4b940b0d777590c7e20bf298f5c16af1ea6ad1b400a1c822a6be192f64f4d954", size = 13747112, upload-time = "2026-08-17T22:54:11.608Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4a/89/c4af146de3d60a32c89fea48d5d34bfd044faaf8957270043a03bd1b462b/onnxruntime-1.29.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:533f8370ce124304e5cb08ab961836cf755631e3dd77adc5f3bbdab70c2b7d99", size = 20826136, upload-time = "2026-08-17T22:54:14.315Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9d/f2/e6bbacd11dfe8d070613261a758795ea128b9fc9bea391a2a7da2e4c7a08/onnxruntime-1.29.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:c1ad3f437153fe77f9d01a08fbaac0beb030e09b8a80ace1603bcf69b6c95481", size = 23138951, upload-time = "2026-08-17T22:54:17.154Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ff/a3/718e1b83096a1bc7b0fc8014c23d4cf795559fe666961cfac4fc038a4871/onnxruntime-1.29.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:e74b278af1d949876f5d91d1268fd6c680e79f2bac194967394eaba9fdf69e7e", size = 21431104, upload-time = "2026-08-17T22:54:20.118Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4e/17/c75e78ddc1fe69b6ebaef7fe88ac83f29bfe10955e3a0d2436d93473c91c/onnxruntime-1.29.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:939e5d65f332e6d399774b2bd0d3559fd8fa629c1e77833db29d968d2384f23d", size = 20818488, upload-time = "2026-08-17T22:54:23.147Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/65/54/9f197c578d3d3d7bea16971e233e5483981228eec73748585cf7b5933403/onnxruntime-1.29.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:6c0c37b92f67ed68dd36221ce0403e1d9bd4f7efce724439978a2597848530e5", size = 23136994, upload-time = "2026-08-17T22:54:26.321Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/24/53/4616a55d2495679cfd0195f968feb3d74fe30e26467d168ee243ac97c089/onnxruntime-1.29.0-cp314-cp314-win_amd64.whl", hash = "sha256:4a3129ae56e70d2618ff773920166916310370a7e3cacb60b9e0e8910092725f", size = 14350643, upload-time = "2026-08-17T22:54:28.794Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0a/0f/c338cb5500a522c7e671a3bb1276f4562404fbecce8a0e274565aa968484/onnxruntime-1.29.0-cp314-cp314-win_arm64.whl", hash = "sha256:e417ef8628dcce310d2d53023e750ea298ec14d4341ae6dc3a572bfd9bc7fa97", size = 14124294, upload-time = "2026-08-17T22:54:31.015Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b8/e7/61064289a9a1301b25c1f0f574fe98aba31c2d388db3c1dbec664f78621f/onnxruntime-1.29.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:11264bb58f7b7cf6af835ab10d36838d73680580820fd6f51d90124a1ca8f449", size = 20826174, upload-time = "2026-08-17T22:54:34.283Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/60/21/d0c04b561b46e9bff89b5f500fb7415b8ca0669f7902204f76ab06bb0c7e/onnxruntime-1.29.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:1ea91cef3b971506e51ae9c37c16d027774ec64994a524ec1bdfb027d68a9832", size = 23138547, upload-time = "2026-08-17T22:54:37.491Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/73/b6/e2fa6dfc8c3cd19639d452c3120fb0e02f5176b4e24ecabb24b5362be725/onnxruntime-1.30.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:f36c87ec504f4f328e968ee322e4ba9682abe66280b3425625afcbed5333fed7", size = 21533037, upload-time = "2026-09-10T16:31:03.582Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4f/1a/4d197d2676a866f6239cd2d06a95b6db40d47dc29d5bec9fb1493a13dd94/onnxruntime-1.30.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:bac3aee7b40acd805659fd81a2acd6096312d5b0f014c97ddfe75fb4c1644627", size = 21332697, upload-time = "2026-09-10T16:31:08.118Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4f/2e/4c96278a99140d0307ccda6fd51e6e9d1ca7c9f6fdd1fa96ff375acfc713/onnxruntime-1.30.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:fd54b314ea385bcecac69ab431f020ba503e3878dad4ebb645fec5a24b041242", size = 23561046, upload-time = "2026-09-10T16:31:11.494Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/66/68/b8aaade8c1883f569228f4b57201cce922bb7d4af6f977d619520a733430/onnxruntime-1.30.0-cp311-cp311-win_amd64.whl", hash = "sha256:0edd0145a6e3fce8a1276491dc784d615e3c58bcb952c9b4e5c876d5c6a12ad7", size = 14309136, upload-time = "2026-09-10T16:31:14.181Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/96/f1/fbda6fb08ac4db6c5c6f7c3e128e4d8fb398b52fe2fc3282c45cadedd18b/onnxruntime-1.30.0-cp311-cp311-win_arm64.whl", hash = "sha256:16506cfa5d218850f2b46e0edce1b952121f2f84f8e0074dafe15f4787f0c8fb", size = 14167408, upload-time = "2026-09-10T16:31:16.505Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/31/6f/48169f2e62b405bff5053cbd1d73fb5ce41ef7ecd13bb3bfcc191e689b8a/onnxruntime-1.30.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:001ed726c9bd5e2bc92faade7d37d889e9606a350b7d5529f0227df2e3bb57fd", size = 21544867, upload-time = "2026-09-10T16:31:19.876Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/16/bd/cbc5b8f91963689fdd622f463508c01d0aa95d3f944747b1e0b1eb2160b8/onnxruntime-1.30.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:6c32a000d5139a38ba9349030b0032e3331acb559d596b22738d9d2b343a2b83", size = 21345202, upload-time = "2026-09-10T16:31:23.361Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/34/35/e7f862dbacbc99fadd9b14a614e49c99bf0f35fd9927a82f096e3de33531/onnxruntime-1.30.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:fa688e7891a6aa206636fe7372e27ee75fd17713289f6b4fc7b190e0a7de9328", size = 23585654, upload-time = "2026-09-10T16:31:26.65Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a6/13/0f1699f6de549c9324bc9112a2a85b14c517904cd11b562a654643b755a1/onnxruntime-1.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:f3501472571f1b1eee50e017851e7929f5ea37312d2d8c2494a19e8fc58b4a38", size = 14311470, upload-time = "2026-09-10T16:31:31.273Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a2/17/02b13e5f51461f0453b18ab854e2d0bc1b6ec353241b05a2ee6b79e35d87/onnxruntime-1.30.0-cp312-cp312-win_arm64.whl", hash = "sha256:dc4c706f1935ebb62356e6a095b047859badd854482c40560888e95c328ed262", size = 14175072, upload-time = "2026-09-10T16:31:34.541Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f0/75/508454c5d01f31641dabc597fe559594c931a520a2673031179319d0afd8/onnxruntime-1.30.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:05e4fc41711d1f4abd19a9124b5be7a65a506cb2670a7f14b16162ef13c58134", size = 21544990, upload-time = "2026-09-10T16:31:37.685Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/89/06/e603c71f43f4fe3fd156a053af79cbed6e27a2c649f0988a67d97fedd39f/onnxruntime-1.30.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:5327cf6aa15a02bad805fac8bd6882a62571e8b72f6f2938a8f37e6bd1966ce9", size = 21344996, upload-time = "2026-09-10T16:31:40.915Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f1/a1/ede48ab5dc54907a2999362777f541e132639fb06628ded1932058aa8a36/onnxruntime-1.30.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:86f940afc801ea9681a4da8af84fbe95e1d9ea7d80903952cc1bfad54faad38f", size = 23585560, upload-time = "2026-09-10T16:31:44.941Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3c/dd/c57c529dbc6dd55eca24b12cfbeab1b6a690de72083824eca689085f55b0/onnxruntime-1.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:4b63041bd623a9a9ac5e353948436c6fa7f43edd12d6b4a4ebc340bca959ba93", size = 14311378, upload-time = "2026-09-10T16:31:48.034Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/11/2f/ef00b45b911e7f2115273a24ed1b43e73c8c6c9ec17f9bfa13f8492581f8/onnxruntime-1.30.0-cp313-cp313-win_arm64.whl", hash = "sha256:c389b6887fc95e0fcb80e89b156bc2cb18e662c29df55e9326fe64140e7d7b4f", size = 14175134, upload-time = "2026-09-10T16:31:50.799Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/92/0a/284fd6fe701c9a8aff39dbc119c37582ca40e9cff1ea05425a7cc8606a02/onnxruntime-1.30.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:e5224ba2b00284cb1c48b3edcd303c109245d66df9ec1b861858fd6de672a38e", size = 21349453, upload-time = "2026-09-10T16:31:53.898Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d6/72/4f4466f8fa1ec267a9ef5e2f3bd175c203d3da0facbdf4049dc93abbe91c/onnxruntime-1.30.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:3f9e002417f1e3bbb31ed43dafa4f22ca1b2b68832244fdee192fcaa1ae19bca", size = 23579837, upload-time = "2026-09-10T16:31:56.82Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6a/03/05c9a9234688757d2876ddecf80bba908561ee11debf125bc1a427ae6f48/onnxruntime-1.30.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:8b6169c16a48429890d2f4a0c774ebf54dfe9066a998514aad0518a16d398547", size = 21545313, upload-time = "2026-09-10T16:31:59.654Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c6/bc/1069e58b24779ba9d2fd479db5ecb3a15a6f49b585107c898819c0789558/onnxruntime-1.30.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:d2184fddb6798136e7c478244391ca82443f5c757f59f15bb9e5ad2da5e03175", size = 21346830, upload-time = "2026-09-10T16:32:02.275Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f1/38/8138eed225c5bc6ddfc05879ecac7dacc63c34b9b6f99be72839c1f6dc49/onnxruntime-1.30.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:8b611d24db2954545ce6bd9acd4670183cb368e4642450de7a9ab6474eb374ec", size = 23586333, upload-time = "2026-09-10T16:32:04.996Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4d/0a/748b86000fbf9518b5dfc5bdc1b924eeaca976203740fd9d30649e568ee6/onnxruntime-1.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:3bdd1752a8502ac1a7ccc6e878d16db6943df574d54ed7f01058a6806ae05be4", size = 14675319, upload-time = "2026-09-10T16:32:07.5Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2f/db/db8e4c0cf6f70f1311060560630fd21763648e01dfbba5195a2592c052ef/onnxruntime-1.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:83d543843cbd352cfa9996a6c7b92f8a480f114a18f95ff2a1acaf6921e85b6d", size = 14569360, upload-time = "2026-09-10T16:32:09.896Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/24/0a/ec0a9d656e39b43887c378a5388b20c3b1e1ee43bded84580b0936466694/onnxruntime-1.30.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:265de607ba6f9814264e1d5d413fa7069d70f48e3049b5e460b3b04bdfcef294", size = 21348433, upload-time = "2026-09-10T16:32:12.772Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/91/f0/40f74b7c00077e1e25627067ed98a70df1fef5c0e21b82849190d312554e/onnxruntime-1.30.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:67ad7f03433b6462c627d0f555dece80e6a26bc71e8542ced35cebd32142d1b7", size = 23579340, upload-time = "2026-09-10T16:32:15.532Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openai"
|
||||
version = "3.9.0"
|
||||
version = "3.13.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "anyio" },
|
||||
@@ -2106,9 +2106,9 @@ dependencies = [
|
||||
{ name = "sniffio" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/14/77/3508ca0f04f42124bb89ab6e3e853c144660b9a680165beb49cd48634bce/openai-3.9.0.tar.gz", hash = "sha256:fea0fe63d04a27e9da588f673e291183fbf4813767e9df743e351bb548afc60b", size = 1481863, upload-time = "2026-09-08T16:43:15.033Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/40/f1/4874c4f7db30121885e6a763e2670b2fe1d76c79ed4732f8acbb51c8d96e/openai-3.13.0.tar.gz", hash = "sha256:a8f87a9b3b9c08eb446d68bd0a80e8ec907c4c35fdea63f4265c7b34b2de3a60", size = 1624217, upload-time = "2026-09-10T19:38:27.597Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/9e/ec/59307303cdb38d89f73555245cec54ab5fe34628c39b78b24ea48d0bbed8/openai-3.9.0-py3-none-any.whl", hash = "sha256:8e0e9a1abea664507f03fdad204579092d0fdf1aff54e8335c2098041d4c0345", size = 1741583, upload-time = "2026-09-08T16:43:12.369Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/63/7f/ee9ebb7c5ab7abf016969faec7c748a91c1fd893078bfe1e5ba5d82e96c2/openai-3.13.0-py3-none-any.whl", hash = "sha256:e35b1f6fe99245e86e37504d9fad1ad2a363807307c424232c1d849bd0666c8e", size = 2009683, upload-time = "2026-09-10T19:38:25.855Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2146,7 +2146,7 @@ resolution-markers = [
|
||||
"(python_full_version < '3.12' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.12' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'linux' and sys_platform != 'win32')",
|
||||
]
|
||||
dependencies = [
|
||||
{ name = "numpy", version = "1.26.4", source = { registry = "https://pypi.org/simple" } },
|
||||
{ name = "numpy", version = "1.26.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/17/06/68c27a523103dad5837dc5b87e71285280c4f098c60e4fe8a8db6486ab09/opencv-python-4.11.0.86.tar.gz", hash = "sha256:03d60ccae62304860d232272e4a4fda93c39d595780cb40b161b310244b736a4", size = 95171956, upload-time = "2025-01-16T13:52:24.737Z" }
|
||||
wheels = [
|
||||
@@ -2175,7 +2175,7 @@ resolution-markers = [
|
||||
"(python_full_version == '3.13.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.13.*' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'linux' and sys_platform != 'win32')",
|
||||
]
|
||||
dependencies = [
|
||||
{ name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" } },
|
||||
{ name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ed/e8/c2959c0f969ea4ae96b55ad10c108c0410de4e8b3550d9941a4cfdd52703/opencv_python-4.14.0.94.tar.gz", hash = "sha256:afadc76a38a07713a4c2b4702da917cf0ed489be57e2a40a34e670189731de7d", size = 96400225, upload-time = "2026-07-28T19:23:09.546Z" }
|
||||
wheels = [
|
||||
@@ -2206,7 +2206,7 @@ resolution-markers = [
|
||||
"(python_full_version < '3.12' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.12' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'linux' and sys_platform != 'win32')",
|
||||
]
|
||||
dependencies = [
|
||||
{ name = "numpy", version = "1.26.4", source = { registry = "https://pypi.org/simple" } },
|
||||
{ name = "numpy", version = "1.26.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/36/2f/5b2b3ba52c864848885ba988f24b7f105052f68da9ab0e693cc7c25b0b30/opencv-python-headless-4.11.0.86.tar.gz", hash = "sha256:996eb282ca4b43ec6a3972414de0e2331f5d9cda2b41091a49739c19fb843798", size = 95177929, upload-time = "2025-01-16T13:53:40.22Z" }
|
||||
wheels = [
|
||||
@@ -2235,7 +2235,7 @@ resolution-markers = [
|
||||
"(python_full_version == '3.13.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.13.*' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'linux' and sys_platform != 'win32')",
|
||||
]
|
||||
dependencies = [
|
||||
{ name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" } },
|
||||
{ name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/67/8a/51ff1c6287be0b6cd3070cf095875da4ff7d6eda38507e6ada5608786a78/opencv_python_headless-4.14.0.94.tar.gz", hash = "sha256:4afa2ea1214453648be88259f035712454faa9039b686de7753569ba8eec1577", size = 96405664, upload-time = "2026-07-28T19:23:48.758Z" }
|
||||
wheels = [
|
||||
@@ -3009,15 +3009,15 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "pyright"
|
||||
version = "1.1.411"
|
||||
version = "1.1.414"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "nodeenv" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/7e/ab/265f7dc69d28113ebba19092e57b075f41543b2ed048429c5f56e2b88eac/pyright-1.1.411.tar.gz", hash = "sha256:d885a0551f2e763b089a02702174e7f4ba77548cddabc972ab86d1f7f1b0f998", size = 4112861, upload-time = "2026-06-25T02:14:06.37Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e1/1b/244c7b710031ada80f27e579ec20d28a2285dfc318fed0339866b1047f12/pyright-1.1.414.tar.gz", hash = "sha256:523c0a97c60da6333234955c277730c9cf4f5bd6d5399e7b7d2b0fc5d3599524", size = 4154638, upload-time = "2026-09-10T12:26:53.181Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/0a/49/385be530a6a5b78d1cbcd5c2e38debc8959a2fc6bdb716f4e581002979fc/pyright-1.1.411-py3-none-any.whl", hash = "sha256:dc7c72a8e2700c55baa127554040e067041ea53ccfd50bf96308cc4291c7d5d9", size = 6181526, upload-time = "2026-06-25T02:14:04.691Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/ba/18b6e682ead424ad24bcc134339ae5d1b931cd9ae260540592a058a91279/pyright-1.1.414-py3-none-any.whl", hash = "sha256:2a6b4b3298c9eec174c5ed83bd338de6eee82df2992f3e1930e6199d381be36f", size = 6225049, upload-time = "2026-09-10T12:26:51.427Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3179,14 +3179,14 @@ name = "pytorch-lightning"
|
||||
version = "2.6.6"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "fsspec", extra = ["http"] },
|
||||
{ name = "lightning-utilities" },
|
||||
{ name = "packaging" },
|
||||
{ name = "pyyaml" },
|
||||
{ name = "torch" },
|
||||
{ name = "torchmetrics" },
|
||||
{ name = "tqdm" },
|
||||
{ name = "typing-extensions" },
|
||||
{ name = "fsspec", extra = ["http"], marker = "python_full_version < '3.13'" },
|
||||
{ name = "lightning-utilities", marker = "python_full_version < '3.13'" },
|
||||
{ name = "packaging", marker = "python_full_version < '3.13'" },
|
||||
{ name = "pyyaml", marker = "python_full_version < '3.13'" },
|
||||
{ name = "torch", marker = "python_full_version < '3.13'" },
|
||||
{ name = "torchmetrics", marker = "python_full_version < '3.13'" },
|
||||
{ name = "tqdm", marker = "python_full_version < '3.13'" },
|
||||
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/97/e4/5025cb42311b4f8cc6d1b12cd81399ca433159f8e3831ab229c5a6c5177e/pytorch_lightning-2.6.6.tar.gz", hash = "sha256:52c52b20522dd3d3feb3bb3aae2dbe3f291e8e836d4111a74cfd72e9df018834", size = 662507, upload-time = "2026-09-10T09:41:16.684Z" }
|
||||
wheels = [
|
||||
@@ -3205,7 +3205,7 @@ resolution-markers = [
|
||||
"(python_full_version < '3.12' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.12' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'linux' and sys_platform != 'win32')",
|
||||
]
|
||||
dependencies = [
|
||||
{ name = "numpy", version = "1.26.4", source = { registry = "https://pypi.org/simple" } },
|
||||
{ name = "numpy", version = "1.26.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/5a/75/50581633d199812205ea8cdd0f6d52f12a624886b74bf1486335b67f01ff/pywavelets-1.9.0.tar.gz", hash = "sha256:148d12203377772bea452a59211d98649c8ee4a05eff019a9021853a36babdc8", size = 3938340, upload-time = "2025-08-04T16:20:04.978Z" }
|
||||
wheels = [
|
||||
@@ -3281,7 +3281,7 @@ resolution-markers = [
|
||||
"(python_full_version == '3.12.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'linux' and sys_platform != 'win32')",
|
||||
]
|
||||
dependencies = [
|
||||
{ name = "numpy", version = "1.26.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" },
|
||||
{ name = "numpy", version = "1.26.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.12.*'" },
|
||||
{ name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/25/84/a9e80e33eb114e78bd2f1829a90d72d8d4d660109c721a8aa2f0d58d7592/pywavelets-1.10.0.tar.gz", hash = "sha256:f3cb8640225a8f3fdcbc3f6f3bd112c78927c9407cbfa65a5ff303990a6a3e32", size = 3948163, upload-time = "2026-09-07T17:15:15.637Z" }
|
||||
@@ -3449,7 +3449,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "remove-ai-watermarks"
|
||||
version = "0.40.6"
|
||||
version = "0.41.1"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "c2pa-python" },
|
||||
@@ -3584,6 +3584,13 @@ qwen-zimage = [
|
||||
{ name = "torchvision" },
|
||||
{ name = "transformers" },
|
||||
]
|
||||
source-classify = [
|
||||
{ name = "huggingface-hub" },
|
||||
{ name = "numpy", version = "1.26.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" },
|
||||
{ name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" },
|
||||
{ name = "opencv-python-headless", version = "4.11.0.86", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" },
|
||||
{ name = "opencv-python-headless", version = "4.14.0.94", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" },
|
||||
]
|
||||
text-draft = [
|
||||
{ name = "huggingface-hub" },
|
||||
{ name = "paddleocr" },
|
||||
@@ -3637,6 +3644,7 @@ requires-dist = [
|
||||
{ name = "huggingface-hub", marker = "extra == 'dev'", specifier = ">=0.20.0" },
|
||||
{ name = "huggingface-hub", marker = "extra == 'lama'", specifier = ">=0.20.0" },
|
||||
{ name = "huggingface-hub", marker = "extra == 'migan'", specifier = ">=0.20.0" },
|
||||
{ name = "huggingface-hub", marker = "extra == 'source-classify'", specifier = ">=0.20.0" },
|
||||
{ name = "huggingface-hub", marker = "extra == 'text-draft'", specifier = ">=0.20.0" },
|
||||
{ name = "invisible-watermark", marker = "extra == 'dev'", specifier = ">=0.2.0" },
|
||||
{ name = "numpy", marker = "python_full_version >= '3.13' and extra == 'pixels'", specifier = ">=2" },
|
||||
@@ -3665,10 +3673,11 @@ requires-dist = [
|
||||
{ name = "remove-ai-watermarks", extras = ["pixels"], marker = "extra == 'classify'" },
|
||||
{ name = "remove-ai-watermarks", extras = ["pixels"], marker = "extra == 'detect'" },
|
||||
{ name = "remove-ai-watermarks", extras = ["pixels"], marker = "extra == 'diffusion'" },
|
||||
{ name = "remove-ai-watermarks", extras = ["pixels"], marker = "extra == 'source-classify'" },
|
||||
{ name = "remove-ai-watermarks", extras = ["pixels"], marker = "extra == 'visible'" },
|
||||
{ name = "remove-ai-watermarks", extras = ["qwen-zimage", "lama"], marker = "extra == 'text-restoration'" },
|
||||
{ name = "remove-ai-watermarks", extras = ["video"], marker = "extra == 'dev'" },
|
||||
{ name = "remove-ai-watermarks", extras = ["video", "heif", "detect", "trustmark", "text-restoration", "migan", "classify"], marker = "extra == 'all'" },
|
||||
{ name = "remove-ai-watermarks", extras = ["video", "heif", "detect", "trustmark", "text-restoration", "migan", "classify", "source-classify"], marker = "extra == 'all'" },
|
||||
{ name = "remove-ai-watermarks", extras = ["visible"], marker = "extra == 'lama'" },
|
||||
{ name = "remove-ai-watermarks", extras = ["visible"], marker = "extra == 'migan'" },
|
||||
{ name = "remove-ai-watermarks", extras = ["visible"], marker = "extra == 'video'" },
|
||||
@@ -3684,7 +3693,7 @@ requires-dist = [
|
||||
{ name = "transformers", marker = "extra == 'diffusion'", specifier = ">=5,<6" },
|
||||
{ name = "trustmark", marker = "python_full_version < '3.13' and extra == 'trustmark'", specifier = ">=0.8.0" },
|
||||
]
|
||||
provides-extras = ["pixels", "heif", "visible", "video", "detect", "diffusion", "qwen-zimage", "text-restoration", "text-draft", "trustmark", "classify", "classify-onnx", "lama", "migan", "dev", "all"]
|
||||
provides-extras = ["pixels", "heif", "visible", "video", "detect", "diffusion", "qwen-zimage", "text-restoration", "text-draft", "trustmark", "classify", "classify-onnx", "source-classify", "lama", "migan", "dev", "all"]
|
||||
|
||||
[[package]]
|
||||
name = "requests"
|
||||
@@ -3725,27 +3734,27 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.16.6"
|
||||
version = "0.16.7"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a4/7c/6adb35d70e7c027e308274557901c7e00fb3407750faf3620c184ae058cb/ruff-0.16.6.tar.gz", hash = "sha256:dcf8a73d2ff77e99dde91244b4da16feba7f14e6beeb4015dee7c5a909e99050", size = 4921251, upload-time = "2026-09-03T16:57:29.037Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/82/bb/5a449b9162e49b139d72f61672bd3ac1d790221f796d3304e2241fff4c58/ruff-0.16.7.tar.gz", hash = "sha256:5f71d004ac1263b22fa39462ac5ae618a4b77d58981af2cc79bf79a29c12b1a6", size = 4924184, upload-time = "2026-09-10T18:04:06.336Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a4/28/9cc1b79639e284ec103f43c88c644db4eb58cbd0ea1ca11f1193435369ac/ruff-0.16.6-py3-none-linux_armv6l.whl", hash = "sha256:61c368c26bf8e973e5ab14a2772de587bc068ea3f9a277f673380749b4898fb8", size = 10015638, upload-time = "2026-09-03T16:56:40.986Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/71/11/627d342ef727ea7794edf74fe23d60a074b02c3acc2e9436684e782286ca/ruff-0.16.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ecf4f068e2e123e43a26e9db4e19524cc56563912404e83bbfca375757e45a32", size = 10220762, upload-time = "2026-09-03T16:56:44.681Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/43/d9/b75668ce41e4c8d073d18d6d08672ba6906ce45d5c06ea4fdb2e84ce3853/ruff-0.16.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:99b62ea33baf130f50368798d841f0d95527b6d817bf31817b65dd058f1d314c", size = 9835082, upload-time = "2026-09-03T16:56:47.142Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/99/97/123ab10b05cde889c107c20f5a9774955104b5552796a2a8584b089ae8eb/ruff-0.16.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7fbf89013f2bb3f6835a6038ff658dc8a1b38c98dc8e724b964168ad4e881876", size = 9949304, upload-time = "2026-09-03T16:56:49.813Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3e/58/a4a2c59dd2e5b85929c912d9cac3056eb9ee8c7e75e9b9fe3e109174966b/ruff-0.16.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:56a67065e22efa6bc4d498299d3bb06c0c90aace8fac2068b5a12f9dc4d8d51d", size = 9840612, upload-time = "2026-09-03T16:56:52.368Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/61/6a/ff8c8626a786c4f49d48ced4a752dadbca65f5263005f9c2416578194694/ruff-0.16.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e25cc89174874b176a157e4428d66761c2c0c006654419bf384f967f361ff1b1", size = 10543465, upload-time = "2026-09-03T16:56:55.089Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ad/bb/c47535923365f337b82e28192e4e9eef2176511007cfd99a62fc22df5dad/ruff-0.16.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0700580ed5303723cb3c11c2f1d2a8913ce77b7ea86646dddb887f5417a9ba70", size = 11267576, upload-time = "2026-09-03T16:56:57.791Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ba/50/e5119a5212b5cd63b51e1f4b25e7bd636a6668fc069a3160b108ad7e3c16/ruff-0.16.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15f1d0b6e165a6e56567befb6629f8209271311d990bae0f37e6d065035ef5f3", size = 10781993, upload-time = "2026-09-03T16:57:00.666Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8b/98/083d8b4ef3c51a0d19db84367791cbe9f44e4b53343d19dfa83556e1cd9a/ruff-0.16.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d72c591a96986ee4268860e2b7235082129ca5e4cb9cbba653a4b57c11893757", size = 10317748, upload-time = "2026-09-03T16:57:03.428Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9a/29/68f7ff2c5ad95f19f00627ac2de95644e25fe47371ea60b2db1fd952315e/ruff-0.16.6-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:65a006baa18f33324325814c864daef03541d51564b98c517610ea756ab7003e", size = 10540096, upload-time = "2026-09-03T16:57:06.182Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c4/f9/79a8f6de85968641d68a7863aeec577551924ef066a990a48ff93167beab/ruff-0.16.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:cd02a7bf1a21a8735228a3e8c95a9dc5cf86bd2a52194f4aaae2a5755b4de0f4", size = 10100494, upload-time = "2026-09-03T16:57:09.194Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d9/e8/b81a22d9b90c00b892ccf2fa2ac36fa95de4c13ab85aea3e73795cfe4651/ruff-0.16.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:31b36f1e5ad85e0737f09d2be4e512e2e283583c14015da3b9dc07359ac0fc88", size = 9843663, upload-time = "2026-09-03T16:57:12.168Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/39/aa/54f516ec5e5a11c4afdceb1c454ebb054ffb96e4f4a1705580b4346abd35/ruff-0.16.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:61029b4ab4aa723fd3064fab96b1d814492596bf0c792679fffcbde1e1679953", size = 10282461, upload-time = "2026-09-03T16:57:15.077Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/52/0b/38d0aa8aa32372b96dc44f97b22e576c4147808271aab7b2cb1e353d4445/ruff-0.16.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:9ac8998457832c2061709d900856b7ad271dace0cb41f346588d540162bfa718", size = 10728808, upload-time = "2026-09-03T16:57:17.797Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5e/e5/9e274e24eeb027640ffc7442f21239f16d17f47acec15ae34f32e03a5c79/ruff-0.16.6-py3-none-win32.whl", hash = "sha256:0b87d9d16fcb63e8018423ca1d50b7260f15cb2da33e30db4baad4183a948c25", size = 10049212, upload-time = "2026-09-03T16:57:20.55Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/22/31/72472449414223ed1a2da236b992adbb1a2ae59e34794574810f60ce068e/ruff-0.16.6-py3-none-win_amd64.whl", hash = "sha256:10d21c51c3495d8eaea7b703a16592117ea6eb1d649e36335aa965ff1173eb39", size = 10556402, upload-time = "2026-09-03T16:57:23.501Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fc/07/d781f8f8e1ac24bef9f3269cf62ffb1407ca24c3a8f12e5e22874f90528c/ruff-0.16.6-py3-none-win_arm64.whl", hash = "sha256:7a976c79b958f94e50a022a19f0f8c87387448020935ec14fc74331bd0a7f2c5", size = 10412850, upload-time = "2026-09-03T16:57:26.416Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e3/b2/c80aeeb7f9e469c0d63a85d2f1ab6e1ebfbe10ea7a8d2438b7e09e3ff09e/ruff-0.16.7-py3-none-linux_armv6l.whl", hash = "sha256:727307773e7c7f9181d3ed3a2484186e56c1fa1874255911c74585eb2c7c19f9", size = 10048917, upload-time = "2026-09-10T18:03:30.28Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7b/96/20bb7bcae008004df52afcb7ac83432d4a467f2c17b672fe46d26be231c5/ruff-0.16.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9d61c258deabf58f34c67bd4bb4d939c7f2e6b5f0e59c1cdd1cf771b11cde929", size = 10242929, upload-time = "2026-09-10T18:03:32.706Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/90/b2/f184b0d5abec02db69cfd7e49b688ae0237554528ca777136c613bf36bee/ruff-0.16.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7ab81118df8945e0193d0240712aa4496573595b75185c3636ed825592a0f728", size = 9847245, upload-time = "2026-09-10T18:03:34.509Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/eb/2d/db1633a641866ed801e34cc6b60ef236c5e16f9b2124ab1d49cc24a5fe4f/ruff-0.16.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c196c968874fc8019da8e7163de7a1a370f111e2309b4b7dfea0fce950198d0", size = 9961780, upload-time = "2026-09-10T18:03:36.618Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4d/98/edea21e1a3e38dbbc3bf6bb068b863b3b06184cf8533a4c7dbbe208a89d5/ruff-0.16.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac8c3bd0a7e10ad31e6ce51e7a99f3cb772e69aecdd6b9ea7e99b362f62a62c0", size = 9866337, upload-time = "2026-09-10T18:03:38.805Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0b/11/a15e60d4c87b214646f116ca9d204475bf993ee1047459bc9a360fd4d6d1/ruff-0.16.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:398d3988edde000b5c75dc1b3f584708da9bc990de069c18909142580fec1af9", size = 10562512, upload-time = "2026-09-10T18:03:40.71Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/29/42/eaff4c9b6d0c7cdf56df313a17e89ae854f5bbc0b0c8f9cce19be0ab7a8f/ruff-0.16.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce05b62b770a8217c4646a9c4139fca00efe8fe5d71f87df2b243ff20d4584d1", size = 11302938, upload-time = "2026-09-10T18:03:42.607Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5d/43/c75aa59a4ec181fe2ec06cab30e198c1c6d107229a9f008ae3a7c16cabd8/ruff-0.16.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af1b576fddb9d9ef2ececfb5fadcd6a624b25070ed85e3cfcfe449fc3ff6a7b9", size = 10840857, upload-time = "2026-09-10T18:03:44.604Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/21/33/81f3da371942ea031105ba679d8d6e28ec1660ccd690a45f42d381161356/ruff-0.16.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ce7f8f22df67c93ed96c717f9128eadb797144ac2bad475cf536f31d6100c55", size = 10370001, upload-time = "2026-09-10T18:03:46.706Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fa/0b/6345fb4dbf6dd0ed1cfe5d18391dc9c3f59cc81622a7b0a65b84b3e730ba/ruff-0.16.7-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:06d0e93d04f392996435ebd600c153f65b47d73fbec2415aa99c5ee5756b3a5f", size = 10548735, upload-time = "2026-09-10T18:03:48.658Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3f/4d/c5576adf511f92a328e5569dda190ecdd430da51f1a649f3a4a2fd73e21e/ruff-0.16.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:142151a5e7b93c1b11111337142f89dd2fbfee92161225c99a97222f22e32656", size = 10108496, upload-time = "2026-09-10T18:03:50.563Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ff/8c/667d83c16199a17a56adc6b0bd4c3beb5b767a2babcd16a56f76f9be7fd6/ruff-0.16.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:e6651f97a342d8b35d54d8991544ca22169b86dc54111cb604666940c431b750", size = 9860136, upload-time = "2026-09-10T18:03:52.621Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/99/75/78d401106731999a1dd20cc5a6961e37e1eb9397a3b589f73f3a5ce146a3/ruff-0.16.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:ef140c6eb935fa9a84c9c607dfb2cb1b85843c192e79265b0c54f35f557ea8e5", size = 10286290, upload-time = "2026-09-10T18:03:55.207Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/68/49/56f9c3a8b755df93a0ad318b2147bf4ef5dae9a7e5ec61c460109c67957f/ruff-0.16.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:53e39506a730fadeee0d998ed5946f30671f0db240c6c7c73bdabbe33604bb6f", size = 10745048, upload-time = "2026-09-10T18:03:57.299Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5f/ea/7f9b938a63ece4bec677ad7f9f7fa02df3383db1949ed93a382441c09a87/ruff-0.16.7-py3-none-win32.whl", hash = "sha256:2ea3470fcebcbc5df2fb0c6f3b90333fa9084c534e0111c038fa4a6ab9f1c4b7", size = 10059082, upload-time = "2026-09-10T18:03:59.632Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/39/11/480a6973a927aa653e1cead6a6416008640e03a99d05b34c0434b8c6c366/ruff-0.16.7-py3-none-win_amd64.whl", hash = "sha256:7ac26aca826e9e21d0f1cb25b54ac660760a9fdd094d3e4df9848232be98cfc6", size = 10593368, upload-time = "2026-09-10T18:04:01.999Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8b/4b/51327018d056f0dad2c2238f26d1fb0f53707a9d91b75dea6d1b3039f136/ruff-0.16.7-py3-none-win_arm64.whl", hash = "sha256:aab7f39e2c9df6c596216070f98eef1207b94f8516cca20c808826974971855b", size = 10412401, upload-time = "2026-09-10T18:04:04.098Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4067,10 +4076,10 @@ name = "torchmetrics"
|
||||
version = "1.9.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "lightning-utilities" },
|
||||
{ name = "numpy", version = "1.26.4", source = { registry = "https://pypi.org/simple" } },
|
||||
{ name = "packaging" },
|
||||
{ name = "torch" },
|
||||
{ name = "lightning-utilities", marker = "python_full_version < '3.13'" },
|
||||
{ name = "numpy", version = "1.26.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" },
|
||||
{ name = "packaging", marker = "python_full_version < '3.13'" },
|
||||
{ name = "torch", marker = "python_full_version < '3.13'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/81/34/39b8b749333db56c0585d7a11fa62a283c087bb1dfc897d69fb8cedbefb1/torchmetrics-1.9.0.tar.gz", hash = "sha256:a488609948600df52d3db4fcdab02e62aab2a85ef34da67037dc3e65b8512faa", size = 581765, upload-time = "2026-03-09T17:41:22.443Z" }
|
||||
wheels = [
|
||||
@@ -4165,13 +4174,13 @@ name = "trustmark"
|
||||
version = "0.9.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "einops" },
|
||||
{ name = "lightning" },
|
||||
{ name = "numpy", version = "1.26.4", source = { registry = "https://pypi.org/simple" } },
|
||||
{ name = "omegaconf" },
|
||||
{ name = "six" },
|
||||
{ name = "torch" },
|
||||
{ name = "torchvision" },
|
||||
{ name = "einops", marker = "python_full_version < '3.13'" },
|
||||
{ name = "lightning", marker = "python_full_version < '3.13'" },
|
||||
{ name = "numpy", version = "1.26.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" },
|
||||
{ name = "omegaconf", marker = "python_full_version < '3.13'" },
|
||||
{ name = "six", marker = "python_full_version < '3.13'" },
|
||||
{ name = "torch", marker = "python_full_version < '3.13'" },
|
||||
{ name = "torchvision", marker = "python_full_version < '3.13'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/53/41/32e84596af94e8696c55060d55eb9eed9d99722a673d12212a1c0c4ef1d2/trustmark-0.9.2.tar.gz", hash = "sha256:bcaa3bc38ead1c0558df81932672f34e306efaed31389da57dbed100e236b28a", size = 63780, upload-time = "2026-09-08T10:58:11.601Z" }
|
||||
|
||||
|
||||
Reference in new issue
Block a user