mirror of
https://github.com/wiltodelta/remove-ai-watermarks.git
synced 2026-08-10 08:00:32 +02:00
Delete every knob the fixed profiles cannot honor
The CLI still advertised --model, --steps, --guidance-scale, --device and a deprecated --auto. Each pinned a value the two surviving profiles fix -- the model stack, the per-stage distilled schedule, CFG 1.0, CUDA -- so the only outcome any of them had was an error raised several frames below the caller, under a message naming an internal profile. A flag whose sole result is a refusal is worse than no flag: it advertises a capability that does not exist, and it lets a wrapper thread a value that will silently do nothing. They are gone from the parser, from InvisibleEngine, and from WatermarkRemover, so the failure is now a TypeError or a Click "No such option" at the point the caller can act on. The install hint was wrong in the same way. is_available() checked torch and diffusers, then told the user to install [diffusion] -- which contains neither DiffSynth nor the Z-Image face stage both profiles run. Following the advice produced a second, different failure. The module list and the extra name now live once in watermark_profiles (REMOVAL_MODULES, INVISIBLE_EXTRA) and are read by both the CLI gate and the remover's precondition, which cannot drift apart because they are the same tuple. The adaptive-polish default moved out of the argument parser. It was resolved by reading Click's parameter source, which put per-profile data in the CLI layer, left the engine declaring the opposite default (False vs True) so a library caller and a CLI caller on one profile got different output, and lost the polish entirely for anything that supplies the flag non-interactively. The flag is now tri-state (default=None) and resolve_adaptive_polish owns the per-profile answer. The seed follows the same rule: the CLI stopped pre-resolving it. Dead code removed with it: six scan_*_video wrappers and the _scan_video helper none of them had a caller for, PNG_METADATA_KEYS, feather_region_composite and the remover region path that was only reachable from a no-caller convenience wrapper, remove_watermark_batch on both layers, try_empty_device_cache, the _generate/_run_qwen_zimage pass-through pair, self.model_id, and the _internal PEP 562 shim that no caller ever went through. get_device now answers cuda or cpu only: mps and xpu travelled one frame to the same CUDA-only refusal while costing a device probe each, and that refusal now names the resolved device, so device=None on a CUDA-less host says 'cpu' rather than 'None'. The XPU wheel index went with them. Docs: README, cli, installation, python-api, supported-signals, known-limitations and module-internals all still described the removed profiles, the CPU/MPS/XPU ladder, a `default`->`sdxl` alias, and the wrong extra. known-limitations still listed the retired SDXL strength ladder as current. scripts/smoke_matrix.py and real_examples_e2e.py drove --device mps. Next release is 0.25.0, not a patch: this removes public parameters and narrows a published extra on top of the released 0.24.0. pre-commit: 1) maintain.sh - exit 0 (1091 tests, Pyright 0 errors, no vulnerabilities); 2) /simplify - 4 agents, 11 findings applied, 2 skipped (dropping the `device` parameter entirely, which raiw-app pins; folding diffsynth into the `diffusion` extra, which video-only callers do not need); 3) docs sync - grepped every removed identifier across README, docs/, scripts/, .claude/; updated 9 docs; 4) CLAUDE.md - added the no-error-only-knobs rule to .claude/rules/development.md Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
bf4bfc1ab7
commit
52b2c115e8
+45
-17
@@ -53,9 +53,19 @@ The decorators for diffusion options are shared by `invisible`, `all`, and
|
||||
`batch`. The runtime help generated by Click is the source of truth for option
|
||||
names and defaults.
|
||||
|
||||
The deprecated `--auto` option does not select a pipeline or change adaptive
|
||||
polishing. [`_resolve_auto_polish`](../src/remove_ai_watermarks/cli.py) emits a
|
||||
warning and returns the explicit polish value unchanged.
|
||||
`--adaptive-polish` is tri-state: it declares `default=None`, so "the user did not
|
||||
choose" is a value the CLI passes through rather than a default it has to invent.
|
||||
`resolve_adaptive_polish` in `watermark_profiles.py` turns that `None` into the
|
||||
profile's answer (off for `qwen-zimage`, whose output already matches the input's
|
||||
detail level; on for `sdxl-zimage`). The same call runs inside
|
||||
`InvisibleEngine.remove_watermark`, so a library caller and a CLI caller on one
|
||||
profile get the same output.
|
||||
|
||||
It used to read Click's parameter source in the CLI instead. That put per-profile
|
||||
data in the argument-parsing layer, left the engine declaring the opposite default,
|
||||
and silently lost the polish for anything supplying the flag non-interactively (an
|
||||
envvar default or a wrapper calling `main()` with a defaulted list is classified
|
||||
`DEFAULT`). The seed follows the same rule: the CLI does not pre-resolve it either.
|
||||
|
||||
Regression coverage:
|
||||
|
||||
@@ -477,25 +487,42 @@ Regression coverage:
|
||||
[`_internal/watermark_profiles.py`](../src/remove_ai_watermarks/_internal/watermark_profiles.py)
|
||||
is the source of truth for:
|
||||
|
||||
- profile aliases;
|
||||
- default model identifiers;
|
||||
- default steps and seeds;
|
||||
- vendor-adaptive strength resolution;
|
||||
- the minimum viable step calculation.
|
||||
- profile names and their underscore spellings;
|
||||
- the fixed seed;
|
||||
- the SDXL global-stage checkpoint id (`SDXL_MODEL_ID`) and the Canny ControlNet id;
|
||||
- strength resolution for both profiles.
|
||||
|
||||
The current profiles are `qwen-zimage` (the default) and `sdxl-zimage`, and both
|
||||
are CUDA-only. `controlnet`, `sdxl`, `qwen` and `default` were removed rather than
|
||||
kept as a CPU path, and are rejected rather than aliased onward. There is no
|
||||
content-dependent automatic router.
|
||||
|
||||
The current profiles are `controlnet`, `sdxl`, `qwen`, and `qwen-zimage`.
|
||||
For serverless cold starts, `InvisibleEngine.preload(global_only=True)` loads the
|
||||
mandatory Qwen stage and YuNet while leaving the optional Z-Image and SAM face
|
||||
mandatory global stage and YuNet while leaving the optional Z-Image and SAM face
|
||||
stack lazy until a face is detected. The default `preload()` still loads every
|
||||
stage.
|
||||
`default` is a legacy alias for `sdxl`. There is no content-dependent automatic
|
||||
router.
|
||||
|
||||
**What is deliberately not a parameter.** Model id, step count, CFG and any
|
||||
non-CUDA device are fixed by the profile, so none of them appears in
|
||||
`WatermarkRemover.__init__`, `remove_watermark`, `InvisibleEngine`, or the CLI.
|
||||
They used to be accepted and then rejected several frames down; a signature that
|
||||
refuses the argument outright fails where the caller can act on it, and stops a
|
||||
wrapper from threading a value that would silently do nothing. The step count and
|
||||
CFG live with the stage that runs them (`GLOBAL_STEPS`, `FACE_STEPS`, `GLOBAL_CFG`,
|
||||
`FACE_CFG` in `qwen_zimage_pipeline.py`). The dtype is likewise profile-owned: see
|
||||
"Face-stage dtype" for what an override cost the last time one existed.
|
||||
|
||||
[`invisible_engine.py`](../src/remove_ai_watermarks/invisible_engine.py) handles
|
||||
image sizing, postprocessing, and the public engine
|
||||
interface. It delegates model execution to
|
||||
[`_internal/watermark_remover.py`](../src/remove_ai_watermarks/_internal/watermark_remover.py).
|
||||
|
||||
`get_device` in that module answers only `cuda` or `cpu`. An `mps` or `xpu` answer
|
||||
would travel one frame to the same CUDA-only refusal while costing a device probe,
|
||||
and reporting it implied an Apple-silicon or Intel-GPU path that does not exist.
|
||||
The refusal names the *resolved* device, so `device=None` on a CUDA-less host says
|
||||
`'cpu'` rather than `'None'`.
|
||||
|
||||
The Python engine and CLI do not have identical defaults for every optional
|
||||
postprocessing argument. Integrations that require reproducibility should pass
|
||||
the relevant values explicitly.
|
||||
@@ -509,9 +536,8 @@ unit-test pass. Exact prompt and edge-map regression guards live in
|
||||
|
||||
Regression coverage:
|
||||
|
||||
- [`test_watermark_profiles.py`](../tests/test_watermark_profiles.py)
|
||||
- [`test_invisible_engine.py`](../tests/test_invisible_engine.py)
|
||||
- [`test_img2img_runner.py`](../tests/test_img2img_runner.py)
|
||||
- [`test_qwen_zimage_pipeline.py`](../tests/test_qwen_zimage_pipeline.py)
|
||||
- [`test_platform.py`](../tests/test_platform.py)
|
||||
|
||||
### CPU offload
|
||||
@@ -712,14 +738,16 @@ Regression coverage:
|
||||
### Tiling
|
||||
|
||||
[`_internal/tiling.py`](../src/remove_ai_watermarks/_internal/tiling.py) contains pure
|
||||
tile planning, feather weights, tile orchestration, and region compositing.
|
||||
tile planning, feather weights, and tile orchestration.
|
||||
|
||||
Tiling engages only when requested and the long side exceeds the tile size.
|
||||
It avoids an explicit full-image downscale but does not make diffusion
|
||||
pixel-preserving. Each tile is still regenerated.
|
||||
|
||||
`feather_region_composite` changes only the requested box and leaves pixels
|
||||
outside it unchanged.
|
||||
It also held a `feather_region_composite` for AI-*enhanced* composites, where only
|
||||
the edited region should change. Nothing ever reached it: the `erase` command
|
||||
inpaints through `region_eraser`, and the remover's `region` argument was only
|
||||
reachable from a module-level convenience wrapper with no callers. Both went.
|
||||
|
||||
Regression coverage:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user