|
|
@ -0,0 +1,60 @@
|
|||
# SD.Next: AGENTS.md Project Guidelines
|
||||
|
||||
SD.Next is a complex codebase with specific patterns and conventions.
|
||||
General app structure is:
|
||||
- Python backend server
|
||||
Uses Torch for model inference, FastAPI for API routes and Gradio for creation of UI components.
|
||||
- JavaScript/CSS frontend
|
||||
|
||||
## Tools
|
||||
|
||||
- `venv` for Python environment management, activated with `source venv/bin/activate` (Linux) or `venv\Scripts\activate` (Windows).
|
||||
venv MUST be activated before running any Python commands or scripts to ensure correct dependencies and environment variables.
|
||||
- `python` 3.10+.
|
||||
- `pyproject.toml` for Python configuration, including linting and type checking settings.
|
||||
- `eslint` configured for both core and UI code.
|
||||
- `pnpm` for managing JavaScript dependencies and scripts, with key commands defined in `package.json`.
|
||||
- `ruff` and `pylint` for Python linting, with configurations in `pyproject.toml` and executed via `pnpm ruff` and `pnpm pylint`.
|
||||
- `pre-commit` hooks which also check line-endings and other formatting issues, configured in `.pre-commit-config.yaml`.
|
||||
|
||||
## Project Structure
|
||||
|
||||
- Entry/startup flow: `webui.sh` -> `launch.py` -> `webui.py` -> modules under `modules/`.
|
||||
- Install: `installer.py` takes care of installing dependencies and setting up the environment.
|
||||
- Core runtime state is centralized in `modules/shared.py` (shared.opts, model state, backend/device state).
|
||||
- API/server routes are under `modules/api/`.
|
||||
- UI codebase is split between base JS in `javascript/` and actual UI in `extensions-builtin/sdnext-modernui/`.
|
||||
- Model and pipeline logic is split between `modules/sd_*` and `pipelines/`.
|
||||
- Additional plug-ins live in `scripts/` and are used only when specified.
|
||||
- Extensions live in `extensions-builtin/` and `extensions/` and are loaded dynamically.
|
||||
- Tests and CLI scripts are under `test/` and `cli/`, with some API smoke checks in `test/full-test.sh`.
|
||||
|
||||
## Code Style
|
||||
|
||||
- Prefer existing project patterns over strict generic style rules;
|
||||
this codebase intentionally allows patterns often flagged in default linters such as allowing long lines, etc.
|
||||
|
||||
## Build And Test
|
||||
|
||||
- Activate environment: `source venv/bin/activate` (always ensure this is active when working with Python code).
|
||||
- Test startup: `python launch.py --test`
|
||||
- Full startup: `python launch.py`
|
||||
- Full lint sequence: `pnpm lint`
|
||||
- Python checks individually: `pnpm ruff`, `pnpm pylint`
|
||||
- JS checks: `pnpm eslint` and `pnpm eslint-ui`
|
||||
|
||||
## Conventions
|
||||
|
||||
- Keep PR-ready changes targeted to `dev` branch.
|
||||
- Use conventions from `CONTRIBUTING`.
|
||||
- Do not include unrelated edits or submodule changes when preparing contributions.
|
||||
- Use existing CLI/API tool patterns in `cli/` and `test/` when adding automation scripts.
|
||||
- Respect environment-driven behavior (`SD_*` flags and options) instead of hardcoding platform/model assumptions.
|
||||
- For startup/init edits, preserve error handling and partial-failure tolerance in parallel scans and extension loading.
|
||||
|
||||
## Pitfalls
|
||||
|
||||
- Initialization order matters: startup paths in `launch.py` and `webui.py` are sensitive to import/load timing.
|
||||
- Shared mutable global state can create subtle regressions; prefer narrow, explicit changes.
|
||||
- Device/backend-specific code paths (**CUDA/ROCm/IPEX/DirectML/OpenVINO**) should not assume one platform.
|
||||
- Scripts and extension loading is dynamic; failures may appear only when specific extensions or models are present.
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
---
|
||||
description: "Use when editing Python core runtime code, startup flow, model loading, API internals, backend/device logic, or shared state in modules and pipelines."
|
||||
name: "Core Runtime Guidelines"
|
||||
applyTo: "launch.py, webui.py, installer.py, modules/**/*.py, pipelines/**/*.py, scripts/**/*.py, extensions-builtin/**/*.py"
|
||||
---
|
||||
# Core Runtime Guidelines
|
||||
|
||||
- Preserve startup ordering and import timing in `launch.py` and `webui.py`; avoid moving initialization steps unless required.
|
||||
- Treat `modules/shared.py` as the source of truth for global runtime state (`shared.opts`, model references, backend/device flags).
|
||||
- Prefer narrow changes with explicit side effects; avoid introducing new cross-module mutable globals.
|
||||
- Keep platform paths neutral: do not assume CUDA-only behavior and preserve ROCm/IPEX/DirectML/OpenVINO compatibility branches.
|
||||
- Keep extension and script loading resilient: when adding startup scans/hooks, preserve partial-failure tolerance and logging.
|
||||
- Follow existing API/server patterns under `modules/api/` and reuse shared queue/state helpers rather than ad-hoc request handling.
|
||||
- Reuse established model-loading and pipeline patterns (`modules/sd_*`, `pipelines/`) instead of creating parallel abstractions.
|
||||
- For substantial Python changes, run at least relevant checks: `npm run ruff` and `npm run pylint` (or narrower equivalents when appropriate).
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
description: "Use when editing frontend UI code, JavaScript, HTML, CSS, localization files, or built-in UI extensions including modernui and kanvas."
|
||||
name: "UI And Frontend Guidelines"
|
||||
applyTo: "javascript/**/*.js, html/**/*.html, html/**/*.css, html/**/*.js, extensions-builtin/sdnext-modernui/**/*, extensions-builtin/sdnext-kanvas/**/*"
|
||||
---
|
||||
# UI And Frontend Guidelines
|
||||
|
||||
- Preserve existing UI behavior and wiring between Gradio/Python endpoints and frontend handlers; do not change payload shapes without backend alignment.
|
||||
- Follow existing project lint and style patterns; prefer consistency with nearby files over introducing new frameworks or architecture.
|
||||
- Keep localization-friendly UI text changes synchronized with locale resources in `html/locale_*.json` when user-facing strings are added or changed.
|
||||
- Avoid bundling unrelated visual refactors with functional fixes; keep UI PRs scoped and reviewable.
|
||||
- For extension UI work, respect each extension's boundaries and avoid cross-extension coupling.
|
||||
- Validate JavaScript changes with `pnpm eslint`; for modern UI extension changes also run `pnpm eslint-ui`.
|
||||
- Maintain mobile compatibility when touching layout or interaction behavior.
|
||||
|
|
@ -2,6 +2,9 @@
|
|||
venv/
|
||||
__pycache__
|
||||
.ruff_cache
|
||||
.vscode/
|
||||
.idea/
|
||||
**/.DS_Store
|
||||
/*.json
|
||||
/*.yaml
|
||||
/params.txt
|
||||
|
|
@ -13,14 +16,11 @@ __pycache__
|
|||
/data/extensions.json
|
||||
/data/cache.json
|
||||
/data/themes.json
|
||||
config_states
|
||||
/data/installer.json
|
||||
/data/rocm.json
|
||||
node_modules
|
||||
pnpm-lock.yaml
|
||||
package-lock.json
|
||||
.history
|
||||
cache
|
||||
**/.DS_Store
|
||||
tunableop_results*.csv
|
||||
|
||||
# all models and temp files
|
||||
*.log
|
||||
|
|
@ -43,31 +43,30 @@ tunableop_results*.csv
|
|||
/*.mp3
|
||||
/*.lnk
|
||||
/*.swp
|
||||
!webui.bat
|
||||
!webui.sh
|
||||
!package.json
|
||||
!requirements.txt
|
||||
|
||||
# all dynamic stuff
|
||||
/extensions/**/*
|
||||
/outputs/**/*
|
||||
/embeddings/**/*
|
||||
/models/**/*
|
||||
/interrogate/**/*
|
||||
/train/log/**/*
|
||||
/textual_inversion/**/*
|
||||
/detected_maps/**/*
|
||||
/tmp
|
||||
/log
|
||||
/cert
|
||||
.vscode/
|
||||
.idea/
|
||||
/localizations
|
||||
.*/
|
||||
cache
|
||||
tunableop_results*.csv
|
||||
|
||||
# force included
|
||||
!webui.bat
|
||||
!webui.sh
|
||||
!package.json
|
||||
!requirements.txt
|
||||
!constraints.txt
|
||||
!/data
|
||||
!/models/VAE-approx
|
||||
!/models/VAE-approx/model.pt
|
||||
!/models/Reference
|
||||
!/models/Reference/**/*
|
||||
|
||||
# copilot instructions
|
||||
!/.github/
|
||||
!/.github/copilot-instructions.md
|
||||
!/.github/instructions/
|
||||
!/.github/instructions/**/*
|
||||
|
|
|
|||
|
|
@ -10,13 +10,9 @@
|
|||
path = extensions-builtin/sd-extension-chainner
|
||||
url = https://github.com/vladmandic/sd-extension-chainner
|
||||
ignore = dirty
|
||||
[submodule "extensions-builtin/stable-diffusion-webui-rembg"]
|
||||
path = extensions-builtin/stable-diffusion-webui-rembg
|
||||
url = https://github.com/vladmandic/sd-extension-rembg
|
||||
ignore = dirty
|
||||
[submodule "extensions-builtin/sdnext-modernui"]
|
||||
path = extensions-builtin/sdnext-modernui
|
||||
url = https://github.com/BinaryQuantumSoul/sdnext-modernui
|
||||
[submodule "extensions-builtin/sdnext-kanvas"]
|
||||
path = extensions-builtin/sdnext-kanvas
|
||||
url = https://github.com/vladmandic/sdnext-kanvas
|
||||
path = extensions-builtin/sdnext-kanvas
|
||||
url = https://github.com/vladmandic/sdnext-kanvas
|
||||
|
|
|
|||
|
|
@ -13,20 +13,17 @@ repos:
|
|||
rev: v6.0.0
|
||||
hooks:
|
||||
- id: check-added-large-files
|
||||
- id: check-case-conflict
|
||||
- id: check-merge-conflict
|
||||
- id: check-symlinks
|
||||
- id: check-illegal-windows-names
|
||||
- id: check-merge-conflict
|
||||
- id: detect-private-key
|
||||
- id: check-builtin-literals
|
||||
- id: check-case-conflict
|
||||
- id: check-illegal-windows-names
|
||||
- id: check-merge-conflict
|
||||
- id: check-symlinks
|
||||
- id: check-yaml
|
||||
- id: check-json
|
||||
- id: check-toml
|
||||
- id: check-xml
|
||||
- id: check-yaml
|
||||
- id: debug-statements
|
||||
- id: detect-private-key
|
||||
- id: end-of-file-fixer
|
||||
- id: mixed-line-ending
|
||||
- id: check-executables-have-shebangs
|
||||
|
|
@ -41,3 +38,13 @@ repos:
|
|||
.*\.md|
|
||||
.github/ISSUE_TEMPLATE/.*\.yml
|
||||
)$
|
||||
- repo: https://github.com/oliv5/pre-commit-indents-to-spaces
|
||||
rev: v0.0.3
|
||||
hooks:
|
||||
- id: indents-to-spaces
|
||||
args: ["--spaces=4"]
|
||||
types: [python]
|
||||
- id: indents-to-spaces
|
||||
args: ["--spaces=2"]
|
||||
types: [file]
|
||||
files: \.(json|js|mjs|css|html|md|yaml|toml|sh)$
|
||||
|
|
|
|||
284
.pylintrc
|
|
@ -1,284 +0,0 @@
|
|||
[MAIN]
|
||||
analyse-fallback-blocks=no
|
||||
clear-cache-post-run=no
|
||||
extension-pkg-allow-list=
|
||||
prefer-stubs=yes
|
||||
extension-pkg-whitelist=
|
||||
fail-on=
|
||||
fail-under=10
|
||||
ignore=CVS
|
||||
ignore-paths=/usr/lib/.*$,
|
||||
venv,
|
||||
.git,
|
||||
.ruff_cache,
|
||||
.vscode,
|
||||
modules/apg,
|
||||
modules/cfgzero,
|
||||
modules/control/proc,
|
||||
modules/control/units,
|
||||
modules/dml,
|
||||
modules/facelib,
|
||||
modules/flash_attn_triton_amd,
|
||||
modules/ggml,
|
||||
modules/hidiffusion,
|
||||
modules/hijack/ddpm_edit.py,
|
||||
modules/intel,
|
||||
modules/intel/ipex,
|
||||
modules/framepack/pipeline,
|
||||
modules/onnx_impl,
|
||||
modules/pag,
|
||||
modules/postprocess/aurasr_arch.py,
|
||||
modules/prompt_parser_xhinker.py,
|
||||
modules/ras,
|
||||
modules/seedvr,
|
||||
modules/rife,
|
||||
modules/schedulers,
|
||||
modules/taesd,
|
||||
modules/teacache,
|
||||
modules/todo,
|
||||
modules/res4lyf,
|
||||
pipelines/bria,
|
||||
pipelines/flex2,
|
||||
pipelines/f_lite,
|
||||
pipelines/hidream,
|
||||
pipelines/hdm,
|
||||
pipelines/meissonic,
|
||||
pipelines/omnigen2,
|
||||
pipelines/segmoe,
|
||||
pipelines/xomni,
|
||||
pipelines/chrono,
|
||||
scripts/consistory,
|
||||
scripts/ctrlx,
|
||||
scripts/daam,
|
||||
scripts/demofusion,
|
||||
scripts/freescale,
|
||||
scripts/infiniteyou,
|
||||
scripts/instantir,
|
||||
scripts/lbm,
|
||||
scripts/layerdiffuse,
|
||||
scripts/mod,
|
||||
scripts/pixelsmith,
|
||||
scripts/differential_diffusion.py,
|
||||
scripts/pulid,
|
||||
scripts/xadapter,
|
||||
repositories,
|
||||
extensions-builtin/sd-extension-chainner/nodes,
|
||||
extensions-builtin/sd-webui-agent-scheduler,
|
||||
extensions-builtin/sdnext-modernui/node_modules,
|
||||
extensions-builtin/sdnext-kanvas/node_modules,
|
||||
ignore-patterns=.*test*.py$,
|
||||
.*_model.py$,
|
||||
.*_arch.py$,
|
||||
.*_model_arch.py*,
|
||||
.*_model_arch_v2.py$,
|
||||
ignored-modules=
|
||||
jobs=8
|
||||
limit-inference-results=100
|
||||
load-plugins=
|
||||
persistent=no
|
||||
py-version=3.10
|
||||
recursive=no
|
||||
source-roots=
|
||||
unsafe-load-any-extension=no
|
||||
|
||||
[BASIC]
|
||||
argument-naming-style=snake_case
|
||||
attr-naming-style=snake_case
|
||||
bad-names=foo, bar, baz, toto, tutu, tata
|
||||
bad-names-rgxs=
|
||||
class-attribute-naming-style=any
|
||||
class-const-naming-style=UPPER_CASE
|
||||
class-naming-style=PascalCase
|
||||
const-naming-style=snake_case
|
||||
docstring-min-length=-1
|
||||
function-naming-style=snake_case
|
||||
good-names=i,j,k,e,ex,ok,p,x,y,id
|
||||
good-names-rgxs=
|
||||
include-naming-hint=no
|
||||
inlinevar-naming-style=any
|
||||
method-naming-style=snake_case
|
||||
module-naming-style=snake_case
|
||||
name-group=
|
||||
no-docstring-rgx=^_
|
||||
property-classes=abc.abstractproperty
|
||||
variable-naming-style=snake_case
|
||||
|
||||
[CLASSES]
|
||||
check-protected-access-in-special-methods=no
|
||||
defining-attr-methods=__init__, __new__,
|
||||
exclude-protected=_asdict,_fields,_replace,_source,_make,os._exit
|
||||
valid-classmethod-first-arg=cls
|
||||
valid-metaclass-classmethod-first-arg=mcs
|
||||
|
||||
[DESIGN]
|
||||
exclude-too-few-public-methods=
|
||||
ignored-parents=
|
||||
max-args=199
|
||||
max-attributes=99
|
||||
max-bool-expr=99
|
||||
max-branches=199
|
||||
max-locals=99
|
||||
max-parents=99
|
||||
max-public-methods=99
|
||||
max-returns=99
|
||||
max-statements=199
|
||||
min-public-methods=1
|
||||
|
||||
[EXCEPTIONS]
|
||||
overgeneral-exceptions=builtins.BaseException,builtins.Exception
|
||||
|
||||
[FORMAT]
|
||||
expected-line-ending-format=
|
||||
ignore-long-lines=^\s*(# )?<?https?://\S+>?$
|
||||
indent-after-paren=4
|
||||
indent-string=' '
|
||||
max-line-length=200
|
||||
max-module-lines=9999
|
||||
single-line-class-stmt=no
|
||||
single-line-if-stmt=no
|
||||
|
||||
[IMPORTS]
|
||||
allow-any-import-level=
|
||||
allow-reexport-from-package=no
|
||||
allow-wildcard-with-all=no
|
||||
deprecated-modules=
|
||||
ext-import-graph=
|
||||
import-graph=
|
||||
int-import-graph=
|
||||
known-standard-library=
|
||||
known-third-party=enchant
|
||||
preferred-modules=
|
||||
|
||||
[LOGGING]
|
||||
logging-format-style=new
|
||||
logging-modules=logging
|
||||
|
||||
[MESSAGES CONTROL]
|
||||
confidence=HIGH,
|
||||
CONTROL_FLOW,
|
||||
INFERENCE,
|
||||
INFERENCE_FAILURE,
|
||||
UNDEFINED
|
||||
# disable=C,R,W
|
||||
disable=abstract-method,
|
||||
bad-inline-option,
|
||||
bare-except,
|
||||
broad-exception-caught,
|
||||
chained-comparison,
|
||||
consider-iterating-dictionary,
|
||||
consider-merging-isinstance,
|
||||
consider-using-dict-items,
|
||||
consider-using-enumerate,
|
||||
consider-using-from-import,
|
||||
consider-using-generator,
|
||||
consider-using-get,
|
||||
consider-using-in,
|
||||
consider-using-max-builtin,
|
||||
consider-using-min-builtin,
|
||||
consider-using-sys-exit,
|
||||
cyclic-import,
|
||||
dangerous-default-value,
|
||||
deprecated-pragma,
|
||||
duplicate-code,
|
||||
file-ignored,
|
||||
import-error,
|
||||
import-outside-toplevel,
|
||||
invalid-name,
|
||||
line-too-long,
|
||||
locally-disabled,
|
||||
logging-fstring-interpolation,
|
||||
missing-class-docstring,
|
||||
missing-function-docstring,
|
||||
missing-module-docstring,
|
||||
no-else-raise,
|
||||
no-else-return,
|
||||
not-callable,
|
||||
pointless-string-statement,
|
||||
raw-checker-failed,
|
||||
simplifiable-if-expression,
|
||||
suppressed-message,
|
||||
too-few-public-methods,
|
||||
too-many-instance-attributes,
|
||||
too-many-locals,
|
||||
too-many-nested-blocks,
|
||||
too-many-positional-arguments,
|
||||
too-many-statements,
|
||||
unidiomatic-typecheck,
|
||||
unknown-option-value,
|
||||
unnecessary-dict-index-lookup,
|
||||
unnecessary-dunder-call,
|
||||
unnecessary-lambda-assigment,
|
||||
unnecessary-lambda,
|
||||
unused-wildcard-import,
|
||||
unpacking-non-sequence,
|
||||
unsubscriptable-object,
|
||||
useless-return,
|
||||
use-dict-literal,
|
||||
use-symbolic-message-instead,
|
||||
useless-suppression,
|
||||
wrong-import-position,
|
||||
enable=c-extension-no-member
|
||||
|
||||
[METHOD_ARGS]
|
||||
timeout-methods=requests.api.delete,requests.api.get,requests.api.head,requests.api.options,requests.api.patch,requests.api.post,requests.api.put,requests.api.request
|
||||
|
||||
[MISCELLANEOUS]
|
||||
notes=FIXME,
|
||||
XXX,
|
||||
TODO
|
||||
notes-rgx=
|
||||
|
||||
[REFACTORING]
|
||||
max-nested-blocks=5
|
||||
never-returning-functions=sys.exit,argparse.parse_error
|
||||
|
||||
[REPORTS]
|
||||
evaluation=max(0, 0 if fatal else 10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10))
|
||||
msg-template=
|
||||
reports=no
|
||||
score=no
|
||||
|
||||
[SIMILARITIES]
|
||||
ignore-comments=yes
|
||||
ignore-docstrings=yes
|
||||
ignore-imports=yes
|
||||
ignore-signatures=yes
|
||||
min-similarity-lines=4
|
||||
|
||||
[SPELLING]
|
||||
max-spelling-suggestions=4
|
||||
spelling-dict=
|
||||
spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy:
|
||||
spelling-ignore-words=
|
||||
spelling-private-dict-file=
|
||||
spelling-store-unknown-words=no
|
||||
|
||||
[STRING]
|
||||
check-quote-consistency=no
|
||||
check-str-concat-over-line-jumps=no
|
||||
|
||||
[TYPECHECK]
|
||||
contextmanager-decorators=contextlib.contextmanager
|
||||
generated-members=numpy.*,logging.*,torch.*,cv2.*
|
||||
ignore-none=yes
|
||||
ignore-on-opaque-inference=yes
|
||||
ignored-checks-for-mixins=no-member,
|
||||
not-async-context-manager,
|
||||
not-context-manager,
|
||||
attribute-defined-outside-init
|
||||
ignored-classes=optparse.Values,thread._local,_thread._local,argparse.Namespace
|
||||
missing-member-hint=yes
|
||||
missing-member-hint-distance=1
|
||||
missing-member-max-choices=1
|
||||
mixin-class-rgx=.*[Mm]ixin
|
||||
signature-mutators=
|
||||
|
||||
[VARIABLES]
|
||||
additional-builtins=
|
||||
allow-global-unused-variables=yes
|
||||
allowed-redefined-builtins=
|
||||
callbacks=cb_,
|
||||
dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_
|
||||
ignored-argument-names=_.*|^ignored_|^unused_
|
||||
init-import=no
|
||||
redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io
|
||||
109
.ruff.toml
|
|
@ -1,109 +0,0 @@
|
|||
line-length = 250
|
||||
indent-width = 4
|
||||
target-version = "py310"
|
||||
exclude = [
|
||||
"venv",
|
||||
".git",
|
||||
".ruff_cache",
|
||||
".vscode",
|
||||
|
||||
"modules/cfgzero",
|
||||
"modules/facelib",
|
||||
"modules/flash_attn_triton_amd",
|
||||
"modules/hidiffusion",
|
||||
"modules/intel/ipex",
|
||||
"modules/pag",
|
||||
"modules/schedulers",
|
||||
"modules/teacache",
|
||||
"modules/seedvr",
|
||||
|
||||
"modules/control/proc",
|
||||
"modules/control/units",
|
||||
"modules/control/units/xs_pipe.py",
|
||||
"modules/postprocess/aurasr_arch.py",
|
||||
|
||||
"pipelines/meissonic",
|
||||
"pipelines/omnigen2",
|
||||
"pipelines/hdm",
|
||||
"pipelines/segmoe",
|
||||
"pipelines/xomni",
|
||||
"pipelines/chrono",
|
||||
|
||||
"scripts/lbm",
|
||||
"scripts/daam",
|
||||
"scripts/xadapter",
|
||||
"scripts/pulid",
|
||||
"scripts/instantir",
|
||||
"scripts/freescale",
|
||||
"scripts/consistory",
|
||||
|
||||
"repositories",
|
||||
|
||||
"extensions-builtin/Lora",
|
||||
"extensions-builtin/sd-extension-chainner/nodes",
|
||||
"extensions-builtin/sd-webui-agent-scheduler",
|
||||
"extensions-builtin/sdnext-modernui/node_modules",
|
||||
]
|
||||
|
||||
[lint]
|
||||
select = [
|
||||
"F",
|
||||
"E",
|
||||
"W",
|
||||
"C",
|
||||
"B",
|
||||
"I",
|
||||
"YTT",
|
||||
"ASYNC",
|
||||
"RUF",
|
||||
"AIR",
|
||||
"NPY",
|
||||
"C4",
|
||||
"T10",
|
||||
"EXE",
|
||||
"ISC",
|
||||
"ICN",
|
||||
"RSE",
|
||||
"TCH",
|
||||
"TID",
|
||||
"INT",
|
||||
"PLE",
|
||||
]
|
||||
ignore = [
|
||||
"B006", # Do not use mutable data structures for argument defaults
|
||||
"B008", # Do not perform function call in argument defaults
|
||||
"B905", # Strict zip() usage
|
||||
"C420", # Unnecessary dict comprehension for iterable; use `dict.fromkeys` instead
|
||||
"C408", # Unnecessary `dict` call
|
||||
"I001", # Import block is un-sorted or un-formatted
|
||||
"E402", # Module level import not at top of file
|
||||
"E501", # Line too long
|
||||
"E721", # Do not compare types, use `isinstance()`
|
||||
"E731", # Do not assign a `lambda` expression, use a `def`
|
||||
"E741", # Ambiguous variable name
|
||||
"F401", # Imported by unused
|
||||
"EXE001", # file with shebang is not marked executable
|
||||
"NPY002", # replace legacy random
|
||||
"RUF005", # Consider iterable unpacking
|
||||
"RUF008", # Do not use mutable default values for dataclass
|
||||
"RUF010", # Use explicit conversion flag
|
||||
"RUF012", # Mutable class attributes
|
||||
"RUF013", # PEP 484 prohibits implicit `Optional`
|
||||
"RUF015", # Prefer `next(...)` over single element slice
|
||||
"RUF046", # Value being cast to `int` is already an integer
|
||||
"RUF059", # Unpacked variables are not used
|
||||
"RUF051", # Prefer pop over del
|
||||
]
|
||||
fixable = ["ALL"]
|
||||
unfixable = []
|
||||
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
||||
|
||||
[format]
|
||||
quote-style = "double"
|
||||
indent-style = "space"
|
||||
skip-magic-trailing-comma = false
|
||||
line-ending = "auto"
|
||||
docstring-code-format = false
|
||||
|
||||
[lint.mccabe]
|
||||
max-complexity = 150
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
# SD.Next: AGENTS.md Project Guidelines
|
||||
|
||||
SD.Next is a complex codebase with specific patterns and conventions.
|
||||
General app structure is:
|
||||
- Python backend server
|
||||
Uses Torch for model inference, FastAPI for API routes and Gradio for creation of UI components.
|
||||
- JavaScript/CSS frontend
|
||||
|
||||
## Tools
|
||||
|
||||
- `venv` for Python environment management, activated with `source venv/bin/activate` (Linux) or `venv\Scripts\activate` (Windows).
|
||||
venv MUST be activated before running any Python commands or scripts to ensure correct dependencies and environment variables.
|
||||
- `python` 3.10+.
|
||||
- `pyproject.toml` for Python configuration, including linting and type checking settings.
|
||||
- `eslint` configured for both core and UI code.
|
||||
- `pnpm` for managing JavaScript dependencies and scripts, with key commands defined in `package.json`.
|
||||
- `ruff` and `pylint` for Python linting, with configurations in `pyproject.toml` and executed via `pnpm ruff` and `pnpm pylint`.
|
||||
- `pre-commit` hooks which also check line-endings and other formatting issues, configured in `.pre-commit-config.yaml`.
|
||||
|
||||
## Project Structure
|
||||
|
||||
- Entry/startup flow: `webui.sh` -> `launch.py` -> `webui.py` -> modules under `modules/`.
|
||||
- Install: `installer.py` takes care of installing dependencies and setting up the environment.
|
||||
- Core runtime state is centralized in `modules/shared.py` (shared.opts, model state, backend/device state).
|
||||
- API/server routes are under `modules/api/`.
|
||||
- UI codebase is split between base JS in `javascript/` and actual UI in `extensions-builtin/sdnext-modernui/`.
|
||||
- Model and pipeline logic is split between `modules/sd_*` and `pipelines/`.
|
||||
- Additional plug-ins live in `scripts/` and are used only when specified.
|
||||
- Extensions live in `extensions-builtin/` and `extensions/` and are loaded dynamically.
|
||||
- Tests and CLI scripts are under `test/` and `cli/`, with some API smoke checks in `test/full-test.sh`.
|
||||
|
||||
## Code Style
|
||||
|
||||
- Prefer existing project patterns over strict generic style rules;
|
||||
this codebase intentionally allows patterns often flagged in default linters such as allowing long lines, etc.
|
||||
|
||||
## Build And Test
|
||||
|
||||
- Activate environment: `source venv/bin/activate` (always ensure this is active when working with Python code).
|
||||
- Test startup: `python launch.py --test`
|
||||
- Full startup: `python launch.py`
|
||||
- Full lint sequence: `pnpm lint`
|
||||
- Python checks individually: `pnpm ruff`, `pnpm pylint`
|
||||
- JS checks: `pnpm eslint` and `pnpm eslint-ui`
|
||||
|
||||
## Conventions
|
||||
|
||||
- Keep PR-ready changes targeted to `dev` branch.
|
||||
- Use conventions from `CONTRIBUTING`.
|
||||
- Do not include unrelated edits or submodule changes when preparing contributions.
|
||||
- Use existing CLI/API tool patterns in `cli/` and `test/` when adding automation scripts.
|
||||
- Respect environment-driven behavior (`SD_*` flags and options) instead of hardcoding platform/model assumptions.
|
||||
- For startup/init edits, preserve error handling and partial-failure tolerance in parallel scans and extension loading.
|
||||
|
||||
## Pitfalls
|
||||
|
||||
- Initialization order matters: startup paths in `launch.py` and `webui.py` are sensitive to import/load timing.
|
||||
- Shared mutable global state can create subtle regressions; prefer narrow, explicit changes.
|
||||
- Device/backend-specific code paths (**CUDA/ROCm/IPEX/DirectML/OpenVINO**) should not assume one platform.
|
||||
- Scripts and extension loading is dynamic; failures may appear only when specific extensions or models are present.
|
||||
225
CHANGELOG.md
|
|
@ -1,28 +1,211 @@
|
|||
# Change Log for SD.Next
|
||||
|
||||
## Update for 2026-02-07
|
||||
## Update for 2026-04-01
|
||||
|
||||
- **Upscalers**
|
||||
- add support for [spandrel](https://github.com/chaiNNer-org/spandrel)
|
||||
upscaling engine with suport for new upscaling model families
|
||||
- add two new ai upscalers: *RealPLKSR NomosWebPhoto* and *RealPLKSR AnimeSharpV2*
|
||||
- add two new interpolation methods: *HQX* and *ICB*
|
||||
### Highlights for 2026-04-01
|
||||
|
||||
This release brings massive code refactoring to modernize codebase and removal of some obsolete features. Leaner & Faster!
|
||||
And since its a bit quieter period when it comes to new models, notable additions would be : *FireRed-Image-Edit*, *SkyWorks-UniPic-3* and new versions of *Anima-Preview*, *Flux-Klein-KV* image models and *LTX 2.3* video model
|
||||
|
||||
If you're on Windows platform, we have a brand new [All-in-one Installer & Launcher](https://github.com/vladmandic/sdnext-launcher): simply download [exe or zip](https://github.com/vladmandic/sdnext-launcher/releases) and done!
|
||||
|
||||
And we have a new (optional) React-based **UI** [Enso](https://github.com/CalamitousFelicitousness/enso)!
|
||||
|
||||
*What else*? Really a lot!
|
||||
New color grading module, updated localization with new languages and improved translations, new CivitAI integration module, new finetunes loader, several new upscalers, improvements to LLM/VLM in captioning and prompt enhance, a lot of new control preprocessors, new realtime server info panel, some new UI themes
|
||||
And major work on API hardening: *security, rate limits, secrets handling, new endpoints*, etc.
|
||||
But also many smaller quality-of-life improvements - for full details, see [ChangeLog](https://github.com/vladmandic/automatic/blob/master/CHANGELOG.md)
|
||||
|
||||
*Note*: Purely due to size of changes, clean install is recommended!
|
||||
Just how big? Some stats: *~530 commits over 880 files*
|
||||
|
||||
[ReadMe](https://github.com/vladmandic/automatic/blob/master/README.md) | [ChangeLog](https://github.com/vladmandic/automatic/blob/master/CHANGELOG.md) | [Docs](https://vladmandic.github.io/sdnext-docs/) | [WiKi](https://github.com/vladmandic/automatic/wiki) | [Discord](https://discord.com/invite/sd-next-federal-batch-inspectors-1101998836328697867) | [Sponsor](https://github.com/sponsors/vladmandic)
|
||||
|
||||
### Details for 2026-04-01
|
||||
|
||||
- **Models**
|
||||
- [Google Flash 3.1 Image](https://ai.google.dev/gemini-api/docs/models/gemini-3-flash-preview) a.k.a. *Nano Banana 2*
|
||||
- [FireRed Image Edit](https://huggingface.co/FireRedTeam/FireRed-Image-Edit-1.0) *1.0 and 1.1*
|
||||
*Note*: FireRed is a fine-tune of Qwen-Image-Edit regardless of its claim as a new base-model
|
||||
- [Skyworks UniPic-3](https://huggingface.co/Skywork/Unipic3), *Consistency and DMD* variants to reference/community section
|
||||
*Note*: UniPic-3 is a fine-tune of Qwen-Image-Edit with new distillation regardless of its claim of major changes
|
||||
- [Anima Preview-v2](https://huggingface.co/circlestone-labs/Anima)
|
||||
- [FLUX.2-Klein-KV](https://huggingface.co/black-forest-labs/FLUX.2-klein-9b-kv), thanks @liutyi
|
||||
- [LTX-Video 2.3](https://huggingface.co/Lightricks/LTX-2.3) in *Full and Distilled* variants and in both original *FP16 and SDNQ-4bit* quantiztion
|
||||
*note* ltx-2.3 is a massive 22B parameters and full model is very large (72GB) so use of pre-quantized variant (32GB) is highly recommended
|
||||
- **Image manipulation**
|
||||
- new **Color grading** module
|
||||
apply basic corrections to your images: brightness,contrast,saturation,shadows,highlights
|
||||
move to professional photo corrections: hue,gamma,sharpness,temperature
|
||||
correct tone: shadows,midtones,highlights
|
||||
add effects: vignette,grain
|
||||
apply professional lut-table using .cube file
|
||||
*hint* color grading is available as step during generate or as processing item for already existing images
|
||||
- **Upscaling**
|
||||
add support for [spandrel](https://github.com/chaiNNer-org/spandrel) engine with suport for new upscaling model families
|
||||
add two new ai upscalers: *RealPLKSR NomosWebPhoto* and *RealPLKSR AnimeSharpV2*
|
||||
add two new **interpolation** methods: *HQX* and *ICB*
|
||||
use high-quality [sharpfin](https://github.com/drhead/Sharpfin) accelerated library
|
||||
extend `chainner` support for additional models
|
||||
- update **Latent corrections** *(former HDR Corrections)*
|
||||
expand allowed models
|
||||
- **Captioning / Prompt Enhance**
|
||||
- new models: **Qwen-3.5**, **Mistral-3** in multiple variations
|
||||
- new models: multiple *heretic* and *abliterated* finetunes for **Qwen, Gemma, Mistral**
|
||||
- **captioning** and **prompt enhance**: add support for all cloud-based Gemini models
|
||||
*3.1/3.0/2.5 pro/flash/flash-lite*
|
||||
- improve captioning and prompt enhance memory handling/offloading
|
||||
- **Features**
|
||||
- pipelines: add **ZImageInpaint**, thanks @CalamitousFelicitousness
|
||||
- add `--remote` command line flag that reduces client/server chatter and improves link stability
|
||||
for long-running generates, useful when running on remote servers
|
||||
- **Secrets** handling: new `secrets.json` and special handling for tokens/keys/passwords
|
||||
used to be treated like any other `config.json` param which can cause security issues
|
||||
- **Control**: many new **pre-processors**
|
||||
*anyline, depth_anything v2, dsine, lotus, marigold normals, oneformer, rtmlib pose, sam2, stablenormal, teed, vitpose*
|
||||
- pipelines: add **ZImageInpaint**
|
||||
- rewritten **CivitAI** module
|
||||
browse/discover mode with sort, period, type/base dropdowns; URL paste; subfolder sorting; auto-browse; dynamic dropdowns
|
||||
- **HiRes**: allow using different lora in refiner prompt
|
||||
- **Nunchaku** models are now listed in networks tab as reference models
|
||||
instead of being used implicitly via quantization
|
||||
- improve image **Metadata** parser for foreign metadata (e.g. XMP)
|
||||
- new **CeeTeeDees image-to-image batch inference**, thanks @resonantsky
|
||||
available in *main interface -> scripts*
|
||||
- **Compute**
|
||||
- **ROCm** advanced configuration and tuning, thanks @resonantsky
|
||||
see *main interface -> scripts -> rocm advanced config*
|
||||
- **ROCm** support for additional AMD GPUs: `gfx103X`, thanks @crashingalexsan
|
||||
- **Cuda** update to `torch=2.11` with `cuda=13.0`
|
||||
- **Ipex** update to `torch==2.11`
|
||||
- **ROCm/Linux** update to `torch==2.11` with `rocm==7.2`
|
||||
- **OpenVINO** update to `torch==2.11` and `openvino==2026.0`
|
||||
- *note* **Cuda** `torch==2.10` removed support for `rtx1000` series and older GPUs
|
||||
use following before first startup to force installation of `torch==2.9.1` with `cuda==12.6`:
|
||||
> `set TORCH_COMMAND='torch==2.9.1 torchvision==0.24.1 torchaudio==2.9.1 --index-url https://download.pytorch.org/whl/cu126'`
|
||||
- *note* **Cuda** `cuda==13.0` requires newer nVidia drivers
|
||||
use following before first startup to force installation of `torch==2.11.0` with `cuda==12.68`:
|
||||
> `set TORCH_COMMAND='torch torchvision --index-url https://download.pytorch.org/whl/cu128`
|
||||
- update installer and support `nunchaku==1.2.1`
|
||||
- **UI**
|
||||
- ui: **themes** add *CTD-NT64Light* and *CTD-NT64Dark*, thanks @resonantsky
|
||||
- ui: **gallery** add option to auto-refresh gallery, thanks @awsr
|
||||
- **Enso** new React-based UI, developed by @CalamitousFelicitousness!
|
||||
with WYSIWYG infinite canvas workspace, command palette, and numerous quality of life improvements across the board
|
||||
enable using `--enso` flag and access using `/enso` endpoint (e.g. <http://localhost:7860/enso>)
|
||||
see [Enso Docs](https://vladmandic.github.io/sdnext-docs/Enso/) and [Enso Home](https://github.com/CalamitousFelicitousness/enso) for details
|
||||
*note* Enso is work-in-progress and alpha-ready
|
||||
- legacy panels **T2I** and **I2I** are disabled by default
|
||||
you can re-enable them in *settings -> ui -> hide legacy tabs*
|
||||
- new panel: **Server Info** with detailed runtime informaton
|
||||
- rename **Scripts** to **Extras** and reorganize to split internal functionality vs external extensions
|
||||
- **Networks** add **UNet/DiT**
|
||||
- **Localization** improved translation quality and new translations locales:
|
||||
*en, en1, en2, en3, en4, hr, es, it, fr, de, pt, ru, zh, ja, ko, hi, ar, bn, ur, id, vi, tr, sr, po, he, xx, yy, qq, tlh*
|
||||
yes, this now includes stuff like *latin, esperanto, arabic, hebrew, klingon* and a lot more!
|
||||
and also introduce some pseudo-locales such as: *techno-babbel*, *for-n00bs*
|
||||
*hint*: click on locale icon in bottom-left corner to cycle through available locales, or set default in *settings -> ui*
|
||||
- **Server settings** new section in *settings*
|
||||
- **Kanvas** add paste image from clipboard
|
||||
- **Themes** add *CTD-NT64Light*, *CTD-NT64Medium* and *CTD-NT64Dark*, thanks @resonantsky
|
||||
- **Themes** add *Vlad-Neomorph*
|
||||
- **Gallery** add option to auto-refresh gallery, thanks @awsr
|
||||
- **Token counters** add per-section display for supported models, thanks @awsr
|
||||
- **Colour grading** add hints for all the functions
|
||||
- **Docs / Wiki**
|
||||
- updates to to compute sections: *AMD-ROCm, AMD-MIOpen, ZLUDA, OpenVINO, nVidia*
|
||||
- updates to core sections: *Installation, Python, Schedulers, Launcher, SDNQ, Video*
|
||||
- added Enso page
|
||||
- **API**
|
||||
- prototype **v2 API** (`/sdapi/v2/`)
|
||||
job-based generation with queue, per-job WebSocket progress, file uploads with TTL, model/network enumeration
|
||||
and a plethora of other improvements *(work-in-progress)*
|
||||
for the time being ships with Enso, which must be enabled wih `--enso` flag on startup for v2 API to be available
|
||||
- **rate limiting**: global for all endpoints, guards against abuse and denial-of-service type of attacks
|
||||
configurable in *settings -> server settings*
|
||||
- new `/sdapi/v1/upload` endpoint with support for both POST with form-data or PUT using raw-bytes
|
||||
- new `/sdapi/v1/torch` endpoint for torch info (backend, version, etc.)
|
||||
- new `/sdapi/v1/gpu` endpoint for GPU info
|
||||
- new `/sdapi/v1/rembg` endpoint for background removal
|
||||
- new `/sdapi/v1/unet` endpoint to list available unets/dits
|
||||
- use rate limiting for api logging
|
||||
- **Obsoleted**
|
||||
- removed support for additional quantization engines: *BitsAndBytes, TorchAO, Optimum-Quanto, NNCF*
|
||||
*note*: SDNQ is quantization engine of choice for SD.Next
|
||||
- removed `flux_enhance` script
|
||||
- **Internal**
|
||||
- refactor: reorganize `cli` scripts
|
||||
- `python==3.13` full support
|
||||
- `python==3.14` initial support
|
||||
see [docs](https://vladmandic.github.io/sdnext-docs/Python/) for details
|
||||
- remove hard-dependnecies:
|
||||
`clip, numba, skimage, torchsde, omegaconf, antlr, patch-ng, patch-ng, astunparse, addict, inflection, jsonmerge, kornia`,
|
||||
`resize-right, voluptuous, yapf, sqlalchemy, invisible-watermark, pi-heif, ftfy, blendmodes, PyWavelets, imp`
|
||||
these are now installed on-demand when needed
|
||||
- bump `huggingface_hub==1.5.0`
|
||||
- bump `transformers==5.3.0`
|
||||
- installer introduce `constraints.txt`
|
||||
- refactor to/from *image/tensor* logic
|
||||
- refactor reorganize `cli` scripts
|
||||
- refactor move tests to dedicated `/test/`
|
||||
- refactor all image handling to `modules/image/`
|
||||
- refactor many params that were server-global are now ui params that are handled per-request
|
||||
*schedulers, todo, tome, etc.*
|
||||
- refactor error handling during `torch.compile`
|
||||
- refactor move `rebmg` to core instead of extensions
|
||||
- remove face restoration
|
||||
- unified command line parsing
|
||||
- reinstall `nunchaku` with `--reinstall` flag
|
||||
- use explicit icon image references in `gallery`, thanks @awsr
|
||||
- launch use threads to async execute non-critical tasks
|
||||
- switch from deprecated `pkg_resources` to `importlib`
|
||||
- modernize typing and type annotations
|
||||
- improve `pydantic==2.x` compatibility
|
||||
- refactor entire logging into separate `modules/logger`
|
||||
- replace `timestamp` based startup checks with state caching
|
||||
- split monolithic `shared` module and introduce `ui_definitions`
|
||||
- modularize all imports and avoid re-imports
|
||||
- use `threading` for deferable operatios
|
||||
- use `threading` for io-independent parallel operations
|
||||
- remove requirements: `clip`, `open-clip`
|
||||
- add new build of `insightface`, thanks @hameerabbasi
|
||||
- reduce use of generators with ui interactor
|
||||
- better subprocess execute, thanks @awsr
|
||||
- better wslopen handling, thanks @awsr
|
||||
- refactor for PEP-484 compliance, thanks @awsr
|
||||
- detect active `venv`
|
||||
- **Obsolete**
|
||||
- remove `normalbae` pre-processor
|
||||
- remove `dwpose` pre-processor
|
||||
- remove `hdm` model support
|
||||
- remove `xadapter` script
|
||||
- remove `codeformer` and `gfpgan` face restorers
|
||||
- **Checks**
|
||||
- switch to `pyproject.toml` for tool configs
|
||||
- update `lint` rules, thanks @awsr
|
||||
- add `ty` to optional lint tooling
|
||||
- add `pyright` to optional lint tooling
|
||||
- **Fixes**
|
||||
- fix: add metadata restore to always-on scripts
|
||||
- fix: improve wildcard weights parsing, thanks @Tillerz
|
||||
- fix: ui gallery cace recursive cleanup, thanks @awsr
|
||||
- fix: `anima` model detection
|
||||
- fix: lora unwanted unload
|
||||
- fix: improve preview error handler
|
||||
- ui `gallery` cache recursive cleanup, thanks @awsr
|
||||
- ui main results pane sizing
|
||||
- ui connection monitor
|
||||
- handle `clip` installer doing unwanted `setuptools` update
|
||||
- cleanup for `uv` installer fallback
|
||||
- add `metadata` restore to always-on scripts
|
||||
- improve `wildcard` weights parsing, thanks @Tillerz
|
||||
- model detection for `anima`
|
||||
- handle `lora` unwanted unload
|
||||
- improve `preview` error handler
|
||||
- handle `gallery` over remote/unsecure connections
|
||||
- fix `ltx2-i2v`
|
||||
- handle missing `preview` image
|
||||
- kandinsky 5 t2i/i2i model type detection
|
||||
- kanvas notify core on image size change
|
||||
- command arg `--reinstall` stricter enforcement
|
||||
- handle `api` state reset
|
||||
- processing upscaler refresh button
|
||||
- simplify and validate `rembg` dependencies
|
||||
- improve video generation progress tracking
|
||||
- handle startup with bad `scripts` more gracefully
|
||||
- thread-safety for `error-limiter`, thanks @awsr
|
||||
- add `lora` support for flux2-klein
|
||||
- fix `lora` change when used with `sdnq`
|
||||
- multiple `sdnq` fixes
|
||||
- handle `taesd` init errors
|
||||
|
||||
## Update for 2026-02-04
|
||||
|
||||
|
|
@ -143,7 +326,7 @@ For full list of changes, see full changelog.
|
|||
available in both *original* and *sdnq-dynamic prequantized* variants
|
||||
thanks @CalamitousFelicitousness
|
||||
*note*: model requires pre-release versions of `transformers` package:
|
||||
> pip install --upgrade git+https://github.com/huggingface/transformers.git
|
||||
> pip install --upgrade git+<https://github.com/huggingface/transformers.git>
|
||||
> ./webui.sh --experimental
|
||||
- [Nunchaku Z-Image Turbo](https://huggingface.co/nunchaku-tech/nunchaku-z-image-turbo)
|
||||
nunchaku optimized z-image turbo
|
||||
|
|
@ -304,7 +487,7 @@ Plus a lot of internal improvements and fixes
|
|||
**Z-Image** is a powerful and highly efficient image generation model with 6B parameters and using Qwen-3 as text encoder
|
||||
unlike most of new models that are far larger, Z-Image architecture allows it to run with good performance even on mid-range hardware
|
||||
*note*: initial release is *Turbo* variant only with *Base* and *Edit* variants to follow
|
||||
- [Kandinsky 5.0 Lite]() is a new 6B model using Qwen-2.5 as text encoder
|
||||
- [Kandinsky 5.0 Lite](https://huggingface.co/kandinskylab/Kandinsky-5.0-I2V-Lite-5s-Diffusers) is a new 6B model using Qwen-2.5 as text encoder
|
||||
it comes in text-to-image and image-edit variants
|
||||
- **Google Gemini Nano Banana** [2.5 Flash](https://blog.google/products/gemini/gemini-nano-banana-examples/) and [3.0 Pro](https://deepmind.google/models/gemini-image/pro/)
|
||||
first cloud-based model directly supported in SD.Next UI
|
||||
|
|
@ -397,9 +580,11 @@ Plus a lot of internal improvements and fixes
|
|||
- control: safe load non-sparse controlnet
|
||||
- control: fix marigold preprocessor with bfloat16
|
||||
- auth: fix password being shown in clear text during login
|
||||
- github: better handling of forks
|
||||
- firefox: remove obsolete checks, thanks @awsr
|
||||
- runai streamer: cleanup logging, thanks @CalamitousFelicitousness
|
||||
- gradio: event handlers, thanks @awsr
|
||||
- seedvr: handle non-cuda environments, thanks @resonantsky
|
||||
|
||||
## Update for 2025-11-06
|
||||
|
||||
|
|
|
|||
113
README.md
|
|
@ -1,8 +1,14 @@
|
|||
<div align="center">
|
||||
<img src="https://github.com/vladmandic/sdnext/raw/master/html/logo-transparent.png" width=200 alt="SD.Next">
|
||||
<img src="https://github.com/vladmandic/sdnext/raw/master/html/logo-transparent.png" width=200 alt="SD.Next: AI art generator logo">
|
||||
|
||||
# SD.Next: All-in-one WebUI for AI generative image and video creation and captioning
|
||||
# SD.Next: All-in-one WebUI
|
||||
|
||||
SD.Next is a powerful, open-source WebUI app for AI image and video generation, built on Stable Diffusion and supporting dozens of advanced models. Create, caption, and process images and videos with a modern, cross-platform interface—perfect for artists, researchers, and AI enthusiasts.
|
||||
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
[](https://discord.gg/VjvR2tabEX)
|
||||
|
|
@ -17,61 +23,63 @@
|
|||
## Table of contents
|
||||
|
||||
- [Documentation](https://vladmandic.github.io/sdnext-docs/)
|
||||
- [SD.Next Features](#sdnext-features)
|
||||
- [Model support](#model-support)
|
||||
- [Platform support](#platform-support)
|
||||
- [SD.Features](#features--capabilities)
|
||||
- [Supported AI Models](#supported-ai-models)
|
||||
- [Supported Platforms & Hardware](#supported-platforms--hardware)
|
||||
- [Getting started](#getting-started)
|
||||
|
||||
## SD.Next Features
|
||||
### Screenshot: Desktop interface
|
||||
|
||||
All individual features are not listed here, instead check [ChangeLog](CHANGELOG.md) for full list of changes
|
||||
- Fully localized:
|
||||
▹ **English | Chinese | Russian | Spanish | German | French | Italian | Portuguese | Japanese | Korean**
|
||||
- Desktop and Mobile support!
|
||||
- Multiple [diffusion models](https://vladmandic.github.io/sdnext-docs/Model-Support/)!
|
||||
- Multi-platform!
|
||||
▹ **Windows | Linux | MacOS | nVidia CUDA | AMD ROCm | Intel Arc / IPEX XPU | DirectML | OpenVINO | ONNX+Olive | ZLUDA**
|
||||
<div align="center">
|
||||
<img src="https://github.com/vladmandic/sdnext/raw/dev/html/screenshot-robot.jpg" alt="SD.Next: AI art generator desktop interface screenshot" width="90%">
|
||||
</div>
|
||||
|
||||
### Screenshot: Mobile interface
|
||||
|
||||
<div align="center">
|
||||
<img src="https://github.com/user-attachments/assets/ced9fe0c-d2c2-46d1-94a7-8f9f2307ce38" alt="SD.Next: AI art generator mobile interface screenshot" width="35%">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
## Features & Capabilities
|
||||
|
||||
SD.Next is feature-rich with a focus on performance, flexibility, and user experience. Key features include:
|
||||
- [Multi-platform](#platform-support!
|
||||
- Many [diffusion models](https://vladmandic.github.io/sdnext-docs/Model-Support/)!
|
||||
- Fully localized to ~15 languages and with support for many [UI themes](https://vladmandic.github.io/sdnext-docs/Themes/)!
|
||||
- [Desktop](#screenshot-desktop-interface) and [Mobile](#screenshot-mobile-interface) support!
|
||||
- Platform specific auto-detection and tuning performed on install
|
||||
- Optimized processing with latest `torch` developments with built-in support for model compile and quantize
|
||||
Compile backends: *Triton | StableFast | DeepCache | OneDiff | TeaCache | etc.*
|
||||
Quantization methods: *SDNQ | BitsAndBytes | Optimum-Quanto | TorchAO / LayerWise*
|
||||
- **Interrogate/Captioning** with 150+ **OpenCLiP** models and 20+ built-in **VLMs**
|
||||
- Built in installer with automatic updates and dependency management
|
||||
|
||||
<br>
|
||||
### Unique features
|
||||
|
||||
**Desktop** interface
|
||||
<div align="center">
|
||||
<img src="https://github.com/user-attachments/assets/d6119a63-6ee5-4597-95f6-29ed0701d3b5" alt="screenshot-modernui-desktop" width="90%">
|
||||
</div>
|
||||
|
||||
**Mobile** interface
|
||||
<div align="center">
|
||||
<img src="https://github.com/user-attachments/assets/ced9fe0c-d2c2-46d1-94a7-8f9f2307ce38" alt="screenshot-modernui-mobile" width="35%">
|
||||
</div>
|
||||
|
||||
For screenshots and information on other available themes, see [Themes](https://vladmandic.github.io/sdnext-docs/Themes/)
|
||||
SD.Next includes many features not found in other WebUIs, such as:
|
||||
- **SDNQ**: State-of-the-Art quantization engine
|
||||
Use pre-quantized or run with quantizaion on-the-fly for up to 4x VRAM reduction with no or minimal quality and performance impact
|
||||
- **Balanced Offload**: Dynamically balance CPU and GPU memory to run larger models on limited hardware
|
||||
- **Captioning** with 150+ **OpenCLiP** models, **Tagger** with **WaifuDiffusion** and **DeepDanbooru** models, and 25+ built-in **VLMs**
|
||||
- **Image Processing** with full image correction color-grading suite of tools
|
||||
|
||||
<br>
|
||||
|
||||
## Model support
|
||||
## Supported AI Models
|
||||
|
||||
SD.Next supports broad range of models: [supported models](https://vladmandic.github.io/sdnext-docs/Model-Support/) and [model specs](https://vladmandic.github.io/sdnext-docs/Models/)
|
||||
|
||||
## Platform support
|
||||
## Supported Platforms & Hardware
|
||||
|
||||
- *nVidia* GPUs using **CUDA** libraries on both *Windows and Linux*
|
||||
- *AMD* GPUs using **ROCm** libraries on *Linux*
|
||||
Support will be extended to *Windows* once AMD releases ROCm for Windows
|
||||
- *AMD* GPUs using **ROCm** libraries on both *Linux and Windows*
|
||||
- *AMD* GPUs on Windows using **ZLUDA** libraries
|
||||
- *Intel Arc* GPUs using **OneAPI** with *IPEX XPU* libraries on both *Windows and Linux*
|
||||
- Any *CPU/GPU* or device compatible with **OpenVINO** libraries on both *Windows and Linux*
|
||||
- Any GPU compatible with *DirectX* on *Windows* using **DirectML** libraries
|
||||
This includes support for AMD GPUs that are not supported by native ROCm libraries
|
||||
- Any GPU or device compatible with **OpenVINO** libraries on both *Windows and Linux*
|
||||
- *Apple M1/M2* on *OSX* using built-in support in Torch with **MPS** optimizations
|
||||
- *ONNX/Olive*
|
||||
- *AMD* GPUs on Windows using **ZLUDA** libraries
|
||||
|
||||
Plus Docker container recipes for: [CUDA, ROCm, Intel IPEX and OpenVINO](https://vladmandic.github.io/sdnext-docs/Docker/)
|
||||
Plus **Docker** container recipes for: [CUDA, ROCm, Intel IPEX and OpenVINO](https://vladmandic.github.io/sdnext-docs/Docker/)
|
||||
|
||||
## Getting started
|
||||
|
||||
|
|
@ -84,21 +92,37 @@ Plus Docker container recipes for: [CUDA, ROCm, Intel IPEX and OpenVINO](https:/
|
|||
> And for platform specific information, check out
|
||||
> [WSL](https://vladmandic.github.io/sdnext-docs/WSL/) | [Intel Arc](https://vladmandic.github.io/sdnext-docs/Intel-ARC/) | [DirectML](https://vladmandic.github.io/sdnext-docs/DirectML/) | [OpenVINO](https://vladmandic.github.io/sdnext-docs/OpenVINO/) | [ONNX & Olive](https://vladmandic.github.io/sdnext-docs/ONNX-Runtime/) | [ZLUDA](https://vladmandic.github.io/sdnext-docs/ZLUDA/) | [AMD ROCm](https://vladmandic.github.io/sdnext-docs/AMD-ROCm/) | [MacOS](https://vladmandic.github.io/sdnext-docs/MacOS-Python/) | [nVidia](https://vladmandic.github.io/sdnext-docs/nVidia/) | [Docker](https://vladmandic.github.io/sdnext-docs/Docker/)
|
||||
|
||||
### Quick Start
|
||||
|
||||
```shell
|
||||
git clone https://github.com/vladmandic/sdnext
|
||||
cd sdnext
|
||||
./webui.sh # Linux/Mac
|
||||
webui.bat # Windows
|
||||
webui.ps1 # PowerShell
|
||||
```
|
||||
|
||||
> [!WARNING]
|
||||
> If you run into issues, check out [troubleshooting](https://vladmandic.github.io/sdnext-docs/Troubleshooting/) and [debugging](https://vladmandic.github.io/sdnext-docs/Debug/) guides
|
||||
|
||||
|
||||
## Community & Support
|
||||
|
||||
If you're unsure how to use a feature, best place to start is [Docs](https://vladmandic.github.io/sdnext-docs/) and if its not there,
|
||||
check [ChangeLog](https://vladmandic.github.io/sdnext-docs/CHANGELOG/) for when feature was first introduced as it will always have a short note on how to use it
|
||||
|
||||
And for any question, reach out on [Discord](https://discord.gg/VjvR2tabEX) or open an [issue](https://github.com/vladmandic/sdnext/issues) or [discussion](https://github.com/vladmandic/sdnext/discussions)
|
||||
|
||||
### Contributing
|
||||
|
||||
Please see [Contributing](CONTRIBUTING) for details on how to contribute to this project
|
||||
And for any question, reach out on [Discord](https://discord.gg/VjvR2tabEX) or open an [issue](https://github.com/vladmandic/sdnext/issues) or [discussion](https://github.com/vladmandic/sdnext/discussions)
|
||||
|
||||
### Credits
|
||||
## License & Credits
|
||||
|
||||
- SD.Next is licensed under the [Apache License 2.0](LICENSE.txt)
|
||||
- Main credit goes to [Automatic1111 WebUI](https://github.com/AUTOMATIC1111/stable-diffusion-webui) for the original codebase
|
||||
- Additional credits are listed in [Credits](https://github.com/AUTOMATIC1111/stable-diffusion-webui/#credits)
|
||||
- Licenses for modules are listed in [Licenses](html/licenses.html)
|
||||
|
||||
### Evolution
|
||||
## Evolution
|
||||
|
||||
<a href="https://star-history.com/#vladmandic/sdnext&Date">
|
||||
<picture width=640>
|
||||
|
|
@ -109,9 +133,4 @@ And for any question, reach out on [Discord](https://discord.gg/VjvR2tabEX) or o
|
|||
|
||||
- [OSS Stats](https://ossinsight.io/analyze/vladmandic/sdnext#overview)
|
||||
|
||||
### Docs
|
||||
|
||||
If you're unsure how to use a feature, best place to start is [Docs](https://vladmandic.github.io/sdnext-docs/) and if its not there,
|
||||
check [ChangeLog](https://vladmandic.github.io/sdnext-docs/CHANGELOG/) for when feature was first introduced as it will always have a short note on how to use it
|
||||
|
||||
<br>
|
||||
|
|
|
|||
105
TODO.md
|
|
@ -2,29 +2,32 @@
|
|||
|
||||
## Internal
|
||||
|
||||
- Update: `transformers==5.0.0`, owner @CalamitousFelicitousness
|
||||
- Deploy: Create executable for SD.Next
|
||||
- Feature: implement `unload_auxiliary_models`
|
||||
- Feature: RIFE update
|
||||
- Feature: RIFE in processing
|
||||
- Feature: SeedVR2 in processing
|
||||
- Feature: Add video models to `Reference`
|
||||
- Deploy: Lite vs Expert mode
|
||||
- Engine: [mmgp](https://github.com/deepbeepmeep/mmgp)
|
||||
- Engine: [sharpfin](https://github.com/drhead/sharpfin) instead of `torchvision`
|
||||
- Engine: `TensorRT` acceleration
|
||||
- Feature: Auto handle scheduler `prediction_type`
|
||||
- Feature: Cache models in memory
|
||||
- Feature: Control tab add overrides handling
|
||||
- Feature: JSON image metadata
|
||||
- Validate: Control tab add overrides handling
|
||||
- Feature: Integrate natural language image search
|
||||
[ImageDB](https://github.com/vladmandic/imagedb)
|
||||
- Feature: LoRA add OMI format support for SD35/FLUX.1, on-hold
|
||||
- Feature: Multi-user support
|
||||
- Feature: Remote Text-Encoder support, sidelined for the moment
|
||||
- Feature: Settings profile manager
|
||||
- Feature: Video tab add full API support
|
||||
- Refactor: Unify *huggingface* and *diffusers* model folders
|
||||
- Refactor: Move `nunchaku` models to refernce instead of internal decision, owner @CalamitousFelicitousness
|
||||
- Refactor: [GGUF](https://huggingface.co/docs/diffusers/main/en/quantization/gguf)
|
||||
- Refactor: move sampler options to settings to config
|
||||
- Refactor: remove `CodeFormer`, owner @CalamitousFelicitousness
|
||||
- Refactor: remove `GFPGAN`, owner @CalamitousFelicitousness
|
||||
- Reimplement `llama` remover for Kanvas, pending end-to-end review of `Kanvas`
|
||||
- Reimplement `llama` remover for Kanvas
|
||||
- Integrate: [Depth3D](https://github.com/vladmandic/sd-extension-depth3d)
|
||||
|
||||
## OnHold
|
||||
|
||||
- Feature: LoRA add OMI format support for SD35/FLUX.1, on-hold
|
||||
- Feature: Remote Text-Encoder support, sidelined for the moment
|
||||
|
||||
## Modular
|
||||
|
||||
|
|
@ -42,8 +45,10 @@
|
|||
TODO: Investigate which models are diffusers-compatible and prioritize!
|
||||
|
||||
### Image-Base
|
||||
|
||||
- [Chroma Zeta](https://huggingface.co/lodestones/Zeta-Chroma): Image and video generator for creative effects and professional filters
|
||||
- [Chroma Radiance](https://huggingface.co/lodestones/Chroma1-Radiance): Pixel-space model eliminating VAE artifacts for high visual fidelity
|
||||
- [Bria FIBO](https://huggingface.co/briaai/FIBO): Fully JSON based
|
||||
- [Liquid](https://github.com/FoundationVision/Liquid): Unified vision-language auto-regressive generation paradigm
|
||||
- [Lumina-DiMOO](https://huggingface.co/Alpha-VLLM/Lumina-DiMOO): Foundational multi-modal generation and understanding via discrete diffusion
|
||||
- [nVidia Cosmos-Predict-2.5](https://huggingface.co/nvidia/Cosmos-Predict2.5-2B): Physics-aware world foundation model for consistent scene prediction
|
||||
|
|
@ -51,14 +56,20 @@ TODO: Investigate which models are diffusers-compatible and prioritize!
|
|||
- [Lumina-DiMOO](https://huggingface.co/Alpha-VLLM/Lumina-DiMOO): foundational multi-modal multi-task generation and understanding
|
||||
|
||||
### Image-Edit
|
||||
|
||||
- [Bria FIBO-Edit](https://huggingface.co/briaai/Fibo-Edit-RMBG): Fully JSON-based instruction-following image editing framework
|
||||
- [Meituan LongCat-Image-Edit-Turbo](https://huggingface.co/meituan-longcat/LongCat-Image-Edit-Turbo):6B instruction-following image editing with high visual consistency
|
||||
- [VIBE Image-Edit](https://huggingface.co/iitolstykh/VIBE-Image-Edit): (Sana+Qwen-VL)Fast visual instruction-based image editing framework
|
||||
- [LucyEdit](https://github.com/huggingface/diffusers/pull/12340):Instruction-guided video editing while preserving motion and identity
|
||||
- [Step1X-Edit](https://github.com/stepfun-ai/Step1X-Edit):Multimodal image editing decoding MLLM tokens via DiT
|
||||
- [OneReward](https://github.com/bytedance/OneReward):Reinforcement learning grounded generative reward model for image editing
|
||||
- [ByteDance DreamO](https://huggingface.co/ByteDance/DreamO): image customization framework for IP adaptation and virtual try-on
|
||||
- [nVidia Cosmos-Transfer-2.5](https://github.com/huggingface/diffusers/pull/13066)
|
||||
|
||||
### Video
|
||||
|
||||
- [LTX-Condition](https://github.com/huggingface/diffusers/pull/13058)
|
||||
- [LTX-Distilled](https://github.com/huggingface/diffusers/pull/12934)
|
||||
- [OpenMOSS MOVA](https://huggingface.co/OpenMOSS-Team/MOVA-720p): Unified foundation model for synchronized high-fidelity video and audio
|
||||
- [Wan family (Wan2.1 / Wan2.2 variants)](https://huggingface.co/Wan-AI/Wan2.2-Animate-14B): MoE-based foundational tools for cinematic T2V/I2V/TI2V
|
||||
example: [Wan2.1-T2V-14B-CausVid](https://huggingface.co/lightx2v/Wan2.1-T2V-14B-CausVid)
|
||||
|
|
@ -81,6 +92,7 @@ TODO: Investigate which models are diffusers-compatible and prioritize!
|
|||
- [Ming (inclusionAI)](https://github.com/inclusionAI/Ming): Unified multimodal model for processing text, audio, image, and video
|
||||
|
||||
### Other/Unsorted
|
||||
|
||||
- [DiffusionForcing](https://github.com/kwsong0113/diffusion-forcing-transformer): Full-sequence diffusion with autoregressive next-token prediction
|
||||
- [Self-Forcing](https://github.com/guandeh17/Self-Forcing): Framework for improving temporal consistency in long-horizon video generation
|
||||
- [SEVA](https://github.com/huggingface/diffusers/pull/11440): Stable Virtual Camera for novel view synthesis and 3D-consistent video
|
||||
|
|
@ -97,8 +109,7 @@ TODO: Investigate which models are diffusers-compatible and prioritize!
|
|||
- [ReNO](https://github.com/ExplainableML/ReNO): Reward-based Noise Optimization to improve text-to-image quality during inference
|
||||
|
||||
### Not Planned
|
||||
- [Bria FIBO](https://huggingface.co/briaai/FIBO): Fully JSON based
|
||||
- [Bria FiboEdit](https://github.com/huggingface/diffusers/commit/d7a1c31f4f85bae5a9e01cdce49bd7346bd8ccd6): Fully JSON based
|
||||
|
||||
- [LoRAdapter](https://github.com/CompVis/LoRAdapter): Not recently updated
|
||||
- [SD3 UltraEdit](https://github.com/HaozheZhao/UltraEdit): Based on SD3
|
||||
- [PowerPaint](https://github.com/open-mmlab/PowerPaint): Based on SD15
|
||||
|
|
@ -109,53 +120,29 @@ TODO: Investigate which models are diffusers-compatible and prioritize!
|
|||
- [DenseDiffusion](https://github.com/naver-ai/DenseDiffusion): Based on SD15
|
||||
- [IC-Light](https://github.com/lllyasviel/IC-Light): Based on SD15
|
||||
|
||||
## Migration
|
||||
|
||||
### Asyncio
|
||||
|
||||
- Policy system is deprecated and will be removed in Python 3.16
|
||||
[Python 3.14 removalsasyncio](https://docs.python.org/3.14/whatsnew/3.14.html#id10)
|
||||
https://docs.python.org/3.14/library/asyncio-policy.html
|
||||
Affected files:
|
||||
[`webui.py`](webui.py)
|
||||
[`cli/sdapi.py`](cli/sdapi.py)
|
||||
Migration:
|
||||
[asyncio.run](https://docs.python.org/3.14/library/asyncio-runner.html#asyncio.run)
|
||||
[asyncio.Runner](https://docs.python.org/3.14/library/asyncio-runner.html#asyncio.Runner)
|
||||
|
||||
### rmtree
|
||||
|
||||
- `onerror` deprecated and replaced with `onexc` in Python 3.12
|
||||
``` python
|
||||
def excRemoveReadonly(func, path, exc: BaseException):
|
||||
import stat
|
||||
shared.log.debug(f'Exception during cleanup: {func} {path} {type(exc).__name__}')
|
||||
if func in (os.rmdir, os.remove, os.unlink) and isinstance(exc, PermissionError):
|
||||
shared.log.debug(f'Retrying cleanup: {path}')
|
||||
os.chmod(path, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO)
|
||||
func(path)
|
||||
# ...
|
||||
try:
|
||||
shutil.rmtree(found.path, ignore_errors=False, onexc=excRemoveReadonly)
|
||||
```
|
||||
|
||||
## Code TODO
|
||||
|
||||
> npm run todo
|
||||
|
||||
- fc: autodetect distilled based on model
|
||||
- fc: autodetect tensor format based on model
|
||||
- hypertile: vae breaks when using non-standard sizes
|
||||
- install: switch to pytorch source when it becomes available
|
||||
- loader: load receipe
|
||||
- loader: save receipe
|
||||
- lora: add other quantization types
|
||||
- lora: add t5 key support for sd35/f1
|
||||
- lora: maybe force imediate quantization
|
||||
- model load: force-reloading entire model as loading transformers only leads to massive memory usage
|
||||
- model load: implement model in-memory caching
|
||||
- modernui: monkey-patch for missing tabs.select event
|
||||
- modules/lora/lora_extract.py:188:9: W0511: TODO: lora: support pre-quantized flux
|
||||
- modules/modular_guiders.py:65:58: W0511: TODO: guiders
|
||||
- processing: remove duplicate mask params
|
||||
- resize image: enable full VAE mode for resize-latent
|
||||
```code
|
||||
installer.py:TODO rocm: switch to pytorch source when it becomes available
|
||||
modules/control/run.py:TODO modernui: monkey-patch for missing tabs.select event
|
||||
modules/history.py:TODO: apply metadata, preview, load/save
|
||||
modules/image/resize.py:TODO resize image: enable full VAE mode for resize-latent
|
||||
modules/lora/lora_apply.py:TODO lora: add other quantization types
|
||||
modules/lora/lora_apply.py:TODO lora: maybe force imediate quantization
|
||||
modules/lora/lora_extract.py:TODO: lora: support pre-quantized flux
|
||||
modules/lora/lora_load.py:TODO lora: add t5 key support for sd35/f1
|
||||
modules/masking.py:TODO: additional masking algorithms
|
||||
modules/modular_guiders.py:TODO: guiders
|
||||
modules/processing_class.py:TODO processing: remove duplicate mask params
|
||||
modules/sd_hijack_hypertile.py:TODO hypertile: vae breaks when using non-standard sizes
|
||||
modules/sd_models.py:TODO model load: implement model in-memory caching
|
||||
modules/sd_samplers_diffusers.py:TODO enso-required
|
||||
modules/sd_unet.py:TODO model load: force-reloading entire model as loading transformers only leads to massive memory usage
|
||||
modules/transformer_cache.py:TODO fc: autodetect distilled based on model
|
||||
modules/transformer_cache.py:TODO fc: autodetect tensor format based on model
|
||||
modules/ui_models_load.py:TODO loader: load receipe
|
||||
modules/ui_models_load.py:TODO loader: save receipe
|
||||
modules/video_models/video_save.py:TODO audio set time-base
|
||||
```
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
#!/usr/bin/env python
|
||||
"""
|
||||
use clip to interrogate image(s)
|
||||
use clip to caption image(s)
|
||||
"""
|
||||
|
||||
import io
|
||||
|
|
@ -43,64 +43,63 @@ def print_summary():
|
|||
log.info({ 'keyword stats': keywords })
|
||||
|
||||
|
||||
async def interrogate(f):
|
||||
async def caption(f):
|
||||
if not filetype.is_image(f):
|
||||
log.info({ 'interrogate skip': f })
|
||||
log.info({ 'caption skip': f })
|
||||
return
|
||||
json = Map({ 'image': encode(f) })
|
||||
log.info({ 'interrogate': f })
|
||||
log.info({ 'caption': f })
|
||||
# run clip
|
||||
json.model = 'clip'
|
||||
res = await sdapi.post('/sdapi/v1/interrogate', json)
|
||||
caption = ""
|
||||
res = await sdapi.post('/sdapi/v1/caption', json)
|
||||
result = ""
|
||||
style = ""
|
||||
if 'caption' in res:
|
||||
caption = res.caption
|
||||
log.info({ 'interrogate caption': caption })
|
||||
if ', by' in caption:
|
||||
style = caption.split(', by')[1].strip()
|
||||
log.info({ 'interrogate style': style })
|
||||
for word in caption.split(' '):
|
||||
result = res.caption
|
||||
log.info({ 'caption result': result })
|
||||
if ', by' in result:
|
||||
style = result.split(', by')[1].strip()
|
||||
log.info({ 'caption style': style })
|
||||
for word in result.split(' '):
|
||||
if word not in exclude:
|
||||
stats['captions'][word] = stats['captions'][word] + 1 if word in stats['captions'] else 1
|
||||
else:
|
||||
log.error({ 'interrogate clip error': res })
|
||||
# run booru
|
||||
json.model = 'deepdanbooru'
|
||||
res = await sdapi.post('/sdapi/v1/interrogate', json)
|
||||
log.error({ 'caption clip error': res })
|
||||
# run tagger (DeepBooru)
|
||||
tagger_req = Map({'image': json.image, 'model': 'deepbooru', 'show_scores': True})
|
||||
res = await sdapi.post('/sdapi/v1/tagger', tagger_req)
|
||||
keywords = {}
|
||||
if 'caption' in res:
|
||||
for term in res.caption.split(', '):
|
||||
term = term.replace('(', '').replace(')', '').replace('\\', '').split(':')
|
||||
if len(term) < 2:
|
||||
continue
|
||||
keywords[term[0]] = term[1]
|
||||
keywords = dict(sorted(keywords.items(), key=lambda x:x[1], reverse=True))
|
||||
for word in keywords.items():
|
||||
stats['keywords'][word[0]] = stats['keywords'][word[0]] + 1 if word[0] in stats['keywords'] else 1
|
||||
log.info({ 'interrogate keywords': keywords })
|
||||
if 'scores' in res and res.scores:
|
||||
keywords = dict(sorted(res.scores.items(), key=lambda x: x[1], reverse=True))
|
||||
for word in keywords:
|
||||
stats['keywords'][word] = stats['keywords'][word] + 1 if word in stats['keywords'] else 1
|
||||
log.info({'caption keywords': keywords})
|
||||
elif 'tags' in res:
|
||||
for tag in res.tags.split(', '):
|
||||
stats['keywords'][tag] = stats['keywords'][tag] + 1 if tag in stats['keywords'] else 1
|
||||
log.info({'caption tags': res.tags})
|
||||
else:
|
||||
log.error({ 'interrogate booru error': res })
|
||||
return caption, keywords, style
|
||||
log.error({'caption tagger error': res})
|
||||
return result, keywords, style
|
||||
|
||||
|
||||
async def main():
|
||||
sys.argv.pop(0)
|
||||
await sdapi.session()
|
||||
if len(sys.argv) == 0:
|
||||
log.error({ 'interrogate': 'no files specified' })
|
||||
log.error({ 'caption': 'no files specified' })
|
||||
for arg in sys.argv:
|
||||
if os.path.exists(arg):
|
||||
if os.path.isfile(arg):
|
||||
await interrogate(arg)
|
||||
await caption(arg)
|
||||
elif os.path.isdir(arg):
|
||||
for root, _dirs, files in os.walk(arg):
|
||||
for f in files:
|
||||
_caption, _keywords, _style = await interrogate(os.path.join(root, f))
|
||||
_caption, _keywords, _style = await caption(os.path.join(root, f))
|
||||
else:
|
||||
log.error({ 'interrogate unknown file type': arg })
|
||||
log.error({ 'caption unknown file type': arg })
|
||||
else:
|
||||
log.error({ 'interrogate file missing': arg })
|
||||
log.error({ 'caption file missing': arg })
|
||||
await sdapi.close()
|
||||
print_summary()
|
||||
|
||||
|
|
@ -24,7 +24,7 @@ def auth():
|
|||
return None
|
||||
|
||||
|
||||
def get(endpoint: str, dct: dict = None):
|
||||
def get(endpoint: str, dct: dict | None = None):
|
||||
req = requests.get(f'{sd_url}{endpoint}', json = dct, timeout=300, verify=False, auth=auth())
|
||||
if req.status_code != 200:
|
||||
return { 'error': req.status_code, 'reason': req.reason, 'url': req.url }
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ def auth():
|
|||
return None
|
||||
|
||||
|
||||
def post(endpoint: str, dct: dict = None):
|
||||
def post(endpoint: str, dct: dict | None = None):
|
||||
req = requests.post(f'{sd_url}{endpoint}', json = dct, timeout=300, verify=False, auth=auth())
|
||||
if req.status_code != 200:
|
||||
return { 'error': req.status_code, 'reason': req.reason, 'url': req.url }
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ def auth():
|
|||
return None
|
||||
|
||||
|
||||
def post(endpoint: str, dct: dict = None):
|
||||
def post(endpoint: str, dct: dict | None = None):
|
||||
req = requests.post(f'{sd_url}{endpoint}', json = dct, timeout=300, verify=False, auth=auth())
|
||||
if req.status_code != 200:
|
||||
return { 'error': req.status_code, 'reason': req.reason, 'url': req.url }
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ def auth():
|
|||
return None
|
||||
|
||||
|
||||
def post(endpoint: str, dct: dict = None):
|
||||
def post(endpoint: str, dct: dict | None = None):
|
||||
req = requests.post(f'{sd_url}{endpoint}', json = dct, timeout=300, verify=False, auth=auth())
|
||||
if req.status_code != 200:
|
||||
return { 'error': req.status_code, 'reason': req.reason, 'url': req.url }
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ def auth():
|
|||
return None
|
||||
|
||||
|
||||
def post(endpoint: str, dct: dict = None):
|
||||
def post(endpoint: str, dct: dict | None = None):
|
||||
req = requests.post(f'{sd_url}{endpoint}', json = dct, timeout=300, verify=False, auth=auth())
|
||||
if req.status_code != 200:
|
||||
return { 'error': req.status_code, 'reason': req.reason, 'url': req.url }
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ def generate(x: int, y: int): # pylint: disable=redefined-outer-name
|
|||
return images
|
||||
|
||||
|
||||
def merge(images: list[Image.Image], horizontal: bool, labels: list[str] = None):
|
||||
def merge(images: list[Image.Image], horizontal: bool, labels: list[str] | None = None):
|
||||
rows = 1 if horizontal else len(images)
|
||||
cols = math.ceil(len(images) / rows)
|
||||
w = max([i.size[0] for i in images])
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ def auth():
|
|||
return None
|
||||
|
||||
|
||||
def post(endpoint: str, dct: dict = None):
|
||||
def post(endpoint: str, dct: dict | None = None):
|
||||
req = requests.post(f'{sd_url}{endpoint}', json = dct, timeout=300, verify=False, auth=auth())
|
||||
if req.status_code != 200:
|
||||
return { 'error': req.status_code, 'reason': req.reason, 'url': req.url }
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ def auth():
|
|||
return None
|
||||
|
||||
|
||||
def get(endpoint: str, dct: dict = None):
|
||||
def get(endpoint: str, dct: dict | None = None):
|
||||
req = requests.get(f'{sd_url}{endpoint}', json=dct, timeout=300, verify=False, auth=auth())
|
||||
if req.status_code != 200:
|
||||
return { 'error': req.status_code, 'reason': req.reason, 'url': req.url }
|
||||
|
|
@ -32,7 +32,7 @@ def get(endpoint: str, dct: dict = None):
|
|||
return req.json()
|
||||
|
||||
|
||||
def post(endpoint: str, dct: dict = None):
|
||||
def post(endpoint: str, dct: dict | None = None):
|
||||
req = requests.post(f'{sd_url}{endpoint}', json = dct, timeout=300, verify=False, auth=auth())
|
||||
if req.status_code != 200:
|
||||
return { 'error': req.status_code, 'reason': req.reason, 'url': req.url }
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ def auth():
|
|||
return None
|
||||
|
||||
|
||||
def post(endpoint: str, payload: dict = None):
|
||||
def post(endpoint: str, payload: dict | None = None):
|
||||
if 'sdapi' not in endpoint:
|
||||
endpoint = f'sdapi/v1/{endpoint}'
|
||||
if 'http' not in endpoint:
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ def auth():
|
|||
return None
|
||||
|
||||
|
||||
def get(endpoint: str, dct: dict = None):
|
||||
def get(endpoint: str, dct: dict | None = None):
|
||||
req = requests.get(f'{sd_url}{endpoint}', json=dct, timeout=300, verify=False, auth=auth())
|
||||
if req.status_code != 200:
|
||||
return { 'error': req.status_code, 'reason': req.reason, 'url': req.url }
|
||||
|
|
@ -34,7 +34,7 @@ def get(endpoint: str, dct: dict = None):
|
|||
return req.json()
|
||||
|
||||
|
||||
def post(endpoint: str, dct: dict = None):
|
||||
def post(endpoint: str, dct: dict | None = None):
|
||||
req = requests.post(f'{sd_url}{endpoint}', json = dct, timeout=300, verify=False, auth=auth())
|
||||
if req.status_code != 200:
|
||||
return { 'error': req.status_code, 'reason': req.reason, 'url': req.url }
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ def auth():
|
|||
return None
|
||||
|
||||
|
||||
def get(endpoint: str, dct: dict = None):
|
||||
def get(endpoint: str, dct: dict | None = None):
|
||||
req = requests.get(f'{sd_url}{endpoint}', json=dct, timeout=300, verify=False, auth=auth())
|
||||
if req.status_code != 200:
|
||||
return { 'error': req.status_code, 'reason': req.reason, 'url': req.url }
|
||||
|
|
@ -34,7 +34,7 @@ def get(endpoint: str, dct: dict = None):
|
|||
return req.json()
|
||||
|
||||
|
||||
def post(endpoint: str, dct: dict = None):
|
||||
def post(endpoint: str, dct: dict | None = None):
|
||||
req = requests.post(f'{sd_url}{endpoint}', json = dct, timeout=300, verify=False, auth=auth())
|
||||
if req.status_code != 200:
|
||||
return { 'error': req.status_code, 'reason': req.reason, 'url': req.url }
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ async function main() {
|
|||
} else {
|
||||
const json = await res.json();
|
||||
console.log('result:', json.info);
|
||||
for (const i in json.images) { // eslint-disable-line guard-for-in
|
||||
for (const i in json.images) {
|
||||
const file = args.output || `/tmp/test-${i}.jpg`;
|
||||
const data = atob(json.images[i]);
|
||||
fs.writeFileSync(file, data, 'binary');
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ async function main() {
|
|||
} else {
|
||||
const json = await res.json();
|
||||
console.log('result:', json.info);
|
||||
for (const i in json.images) { // eslint-disable-line guard-for-in
|
||||
for (const i in json.images) {
|
||||
const f = `/tmp/test-${i}.jpg`;
|
||||
fs.writeFileSync(f, atob(json.images[i]), 'binary');
|
||||
console.log('image saved:', f);
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ def auth():
|
|||
return None
|
||||
|
||||
|
||||
def post(endpoint: str, dct: dict = None):
|
||||
def post(endpoint: str, dct: dict | None = None):
|
||||
req = requests.post(f'{sd_url}{endpoint}', json = dct, timeout=300, verify=False, auth=auth())
|
||||
if req.status_code != 200:
|
||||
return { 'error': req.status_code, 'reason': req.reason, 'url': req.url }
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ def auth():
|
|||
return None
|
||||
|
||||
|
||||
def get(endpoint: str, dct: dict = None):
|
||||
def get(endpoint: str, dct: dict | None = None):
|
||||
req = requests.get(f'{sd_url}{endpoint}', json=dct, timeout=300, verify=False, auth=auth())
|
||||
if req.status_code != 200:
|
||||
return { 'error': req.status_code, 'reason': req.reason, 'url': req.url }
|
||||
|
|
@ -32,7 +32,7 @@ def get(endpoint: str, dct: dict = None):
|
|||
return req.json()
|
||||
|
||||
|
||||
def post(endpoint: str, dct: dict = None):
|
||||
def post(endpoint: str, dct: dict | None = None):
|
||||
req = requests.post(f'{sd_url}{endpoint}', json = dct, timeout=300, verify=False, auth=auth())
|
||||
if req.status_code != 200:
|
||||
return { 'error': req.status_code, 'reason': req.reason, 'url': req.url }
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ def auth():
|
|||
return None
|
||||
|
||||
|
||||
def get(endpoint: str, dct: dict = None):
|
||||
def get(endpoint: str, dct: dict | None = None):
|
||||
req = requests.get(f'{sd_url}{endpoint}', json=dct, timeout=300, verify=False, auth=auth())
|
||||
if req.status_code != 200:
|
||||
return { 'error': req.status_code, 'reason': req.reason, 'url': req.url }
|
||||
|
|
@ -32,7 +32,7 @@ def get(endpoint: str, dct: dict = None):
|
|||
return req.json()
|
||||
|
||||
|
||||
def post(endpoint: str, dct: dict = None):
|
||||
def post(endpoint: str, dct: dict | None = None):
|
||||
req = requests.post(f'{sd_url}{endpoint}', json = dct, timeout=300, verify=False, auth=auth())
|
||||
if req.status_code != 200:
|
||||
return { 'error': req.status_code, 'reason': req.reason, 'url': req.url }
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ def auth():
|
|||
return None
|
||||
|
||||
|
||||
def post(endpoint: str, dct: dict = None):
|
||||
def post(endpoint: str, dct: dict | None = None):
|
||||
req = requests.post(f'{sd_url}{endpoint}', json = dct, timeout=300, verify=False, auth=auth())
|
||||
if req.status_code != 200:
|
||||
return { 'error': req.status_code, 'reason': req.reason, 'url': req.url }
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ def auth():
|
|||
return None
|
||||
|
||||
|
||||
def get(endpoint: str, dct: dict = None):
|
||||
def get(endpoint: str, dct: dict | None = None):
|
||||
req = requests.get(f'{sd_url}{endpoint}', json = dct, timeout=300, verify=False, auth=auth())
|
||||
if req.status_code != 200:
|
||||
return { 'error': req.status_code, 'reason': req.reason, 'url': req.url }
|
||||
|
|
|
|||
|
|
@ -99,10 +99,10 @@ def search_civitai(
|
|||
types:str = '', # (Checkpoint, TextualInversion, Hypernetwork, AestheticGradient, LORA, Controlnet, Poses)
|
||||
sort:str = '', # (Highest Rated, Most Downloaded, Newest)
|
||||
period:str = '', # (AllTime, Year, Month, Week, Day)
|
||||
nsfw:bool = None, # optional:bool
|
||||
nsfw:bool | None = None, # optional:bool
|
||||
limit:int = 0,
|
||||
base:list[str] = [], # list
|
||||
token:str = None,
|
||||
token:str | None = None,
|
||||
exact:bool = True,
|
||||
):
|
||||
import requests
|
||||
|
|
@ -113,6 +113,11 @@ def search_civitai(
|
|||
return []
|
||||
|
||||
t0 = time.time()
|
||||
import re
|
||||
url_match = re.match(r'https?://civitai\.com/models/(\d+)', query.strip())
|
||||
if url_match:
|
||||
query = url_match.group(1)
|
||||
log.info(f'CivitAI: extracted model id={query} from URL')
|
||||
dct = { 'query': query }
|
||||
if len(tag) > 0:
|
||||
dct['tag'] = tag
|
||||
|
|
@ -164,7 +169,7 @@ def search_civitai(
|
|||
return exact_models if len(exact_models) > 0 else models
|
||||
|
||||
|
||||
def models_to_dct(all_models:list, model_id:int=None):
|
||||
def models_to_dct(all_models:list, model_id:int | None=None):
|
||||
dct = []
|
||||
for model in all_models:
|
||||
if model_id is not None and model.id != model_id:
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ def pil_to_b64(img: Image, size: int, quality: int):
|
|||
return f'data:image/jpeg;base64,{b64encoded}'
|
||||
|
||||
|
||||
def post(endpoint: str, dct: dict = None):
|
||||
def post(endpoint: str, dct: dict | None = None):
|
||||
req = requests.post(endpoint, json = dct, timeout=300, verify=False)
|
||||
if req.status_code != 200:
|
||||
return { 'error': req.status_code, 'reason': req.reason, 'url': req.url }
|
||||
|
|
|
|||
|
|
@ -24,10 +24,7 @@
|
|||
{
|
||||
"upscaler_1": "SwinIR_4x",
|
||||
"upscaler_2": "None",
|
||||
"upscaling_resize": 0,
|
||||
"gfpgan_visibility": 0,
|
||||
"codeformer_visibility": 0,
|
||||
"codeformer_weight": 0.5
|
||||
"upscaling_resize": 0
|
||||
},
|
||||
"options":
|
||||
{
|
||||
|
|
|
|||
|
|
@ -65,8 +65,6 @@ def exif(info, i = None, op = 'generate'):
|
|||
seed = ', '.join([str(x) for x in seed]) # int list to str list to single str
|
||||
template = '{prompt} | negative {negative_prompt} | seed {s} | steps {steps} | cfgscale {cfg_scale} | sampler {sampler_name} | batch {batch_size} | timestamp {job_timestamp} | model {model} | vae {vae}'.format(s = seed, model = sd.options['sd_model_checkpoint'], vae = sd.options['sd_vae'], **info) # pylint: disable=consider-using-f-string
|
||||
if op == 'upscale':
|
||||
template += ' | faces gfpgan' if sd.upscale.gfpgan_visibility > 0 else ''
|
||||
template += ' | faces codeformer' if sd.upscale.codeformer_visibility > 0 else ''
|
||||
template += ' | upscale {resize}x {upscaler}'.format(resize = sd.upscale.upscaling_resize, upscaler = sd.upscale.upscaler_1) if sd.upscale.upscaler_1 != 'None' else '' # pylint: disable=consider-using-f-string
|
||||
template += ' | upscale {resize}x {upscaler}'.format(resize = sd.upscale.upscaling_resize, upscaler = sd.upscale.upscaler_2) if sd.upscale.upscaler_2 != 'None' else '' # pylint: disable=consider-using-f-string
|
||||
if op == 'grid':
|
||||
|
|
@ -309,7 +307,6 @@ def args(): # parse cmd arguments
|
|||
sd.generate.height = params.height if params.height > 0 else sd.generate.height
|
||||
sd.generate.steps = params.steps if params.steps > 0 else sd.generate.steps
|
||||
sd.upscale.upscaling_resize = params.upscale if params.upscale > 0 else sd.upscale.upscaling_resize
|
||||
sd.upscale.codeformer_visibility = 1 if params.detailer else sd.upscale.codeformer_visibility
|
||||
sd.options.sd_vae = params.vae if params.vae != '' else sd.options.sd_vae
|
||||
sd.options.sd_model_checkpoint = params.model if params.model != '' else sd.options.sd_model_checkpoint
|
||||
sd.upscale.upscaler_1 = 'SwinIR_4x' if params.upscale > 1 else sd.upscale.upscaler_1
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ if __name__ == "__main__":
|
|||
full=True,
|
||||
limit=100,
|
||||
sort="downloads",
|
||||
direction=-1,
|
||||
)
|
||||
res = sorted(res, key=lambda x: x.id)
|
||||
exact = [m for m in res if keyword.lower() in m.id.lower()]
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ class ImageDB:
|
|||
def __init__(self,
|
||||
name:str='db',
|
||||
fmt:str='json',
|
||||
cache_dir:str=None,
|
||||
cache_dir:str | None=None,
|
||||
dtype:torch.dtype=torch.float16,
|
||||
device:torch.device=torch.device('cpu'),
|
||||
model:str='openai/clip-vit-large-patch14', # 'facebook/dinov2-small'
|
||||
|
|
@ -123,8 +123,8 @@ class ImageDB:
|
|||
self.df = rec
|
||||
self.index.add(embed)
|
||||
|
||||
def search(self, filename: str = None, metadata: str = None, embed: np.ndarray = None, k=10, d=1.0): # search by filename/metadata/prompt-embed/image-embed
|
||||
def dct(record: pd.DataFrame, mode: str, distance: float = None):
|
||||
def search(self, filename: str | None = None, metadata: str | None = None, embed: np.ndarray = None, k=10, d=1.0): # search by filename/metadata/prompt-embed/image-embed
|
||||
def dct(record: pd.DataFrame, mode: str, distance: float | None = None):
|
||||
if distance is not None:
|
||||
return {'type': mode, 'filename': record[1]['filename'], 'metadata': record[1]['metadata'], 'distance': round(distance, 2)}
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -1,67 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
// script used to localize sdnext ui and hints to multiple languages using google gemini ai
|
||||
|
||||
const fs = require('node:fs');
|
||||
|
||||
const { GoogleGenerativeAI } = require('@google/generative-ai');
|
||||
|
||||
const api_key = process.env.GOOGLE_AI_API_KEY;
|
||||
const model = 'gemini-2.5-flash';
|
||||
const prompt = `Translate attached JSON from English to {language} using following rules: fields id, label and reload should be preserved from original, field localized should be a translated version of field label and field hint should be translated in-place.
|
||||
if field is less than 3 characters, do not translate it and keep it as is.
|
||||
Every JSON entry should have id, label, localized, reload and hint fields.
|
||||
Output should be pure JSON without any additional text. To better match translation, context of the text is related to Stable Diffusion and topic of Generative AI.`;
|
||||
const languages = {
|
||||
hr: 'Croatian',
|
||||
de: 'German',
|
||||
es: 'Spanish',
|
||||
fr: 'French',
|
||||
it: 'Italian',
|
||||
pt: 'Portuguese',
|
||||
zh: 'Chinese',
|
||||
ja: 'Japanese',
|
||||
ko: 'Korean',
|
||||
ru: 'Russian',
|
||||
};
|
||||
const chunkLines = 100;
|
||||
|
||||
async function localize() {
|
||||
if (!api_key || api_key.length < 10) {
|
||||
console.error('localize: set GOOGLE_AI_API_KEY env variable with your API key');
|
||||
process.exit();
|
||||
}
|
||||
const genAI = new GoogleGenerativeAI(api_key);
|
||||
const instance = genAI.getGenerativeModel({ model });
|
||||
const raw = fs.readFileSync('html/locale_en.json');
|
||||
const json = JSON.parse(raw);
|
||||
for (const locale of Object.keys(languages)) {
|
||||
const lang = languages[locale];
|
||||
const target = prompt.replace('{language}', lang).trim();
|
||||
const output = {};
|
||||
const fn = `html/locale_${locale}.json`;
|
||||
for (const section of Object.keys(json)) {
|
||||
const data = json[section];
|
||||
output[section] = [];
|
||||
for (let i = 0; i < data.length; i += chunkLines) {
|
||||
let markdown;
|
||||
try {
|
||||
const chunk = data.slice(i, i + chunkLines);
|
||||
const result = await instance.generateContent([target, JSON.stringify(chunk)]);
|
||||
markdown = result.response.text();
|
||||
const text = markdown.replaceAll('```', '').replace(/^.*\n/, '');
|
||||
const parsed = JSON.parse(text);
|
||||
output[section].push(...parsed);
|
||||
console.log(`localize: locale=${locale} lang=${lang} section=${section} chunk=${chunk.length} output=${output[section].length} fn=${fn}`);
|
||||
} catch (err) {
|
||||
console.error('localize:', err);
|
||||
console.error('localize input:', { target, section, i });
|
||||
console.error('localize output:', { markdown });
|
||||
}
|
||||
}
|
||||
const txt = JSON.stringify(output, null, 2);
|
||||
fs.writeFileSync(fn, txt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
localize();
|
||||
|
|
@ -13,9 +13,8 @@ def get_nvidia_smi(output='dict'):
|
|||
if smi is None:
|
||||
log.error("nvidia-smi not found")
|
||||
return None
|
||||
result = subprocess.run(f'"{smi}" -q -x', shell=True, check=False, env=os.environ, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
xml = result.stdout.decode(encoding="utf8", errors="ignore")
|
||||
d = xmltodict.parse(xml)
|
||||
result = subprocess.run(f'"{smi}" -q -x', shell=True, check=False, env=os.environ, capture_output=True, text=True)
|
||||
d = xmltodict.parse(result.stdout)
|
||||
if 'nvidia_smi_log' in d:
|
||||
d = d['nvidia_smi_log']
|
||||
if 'gpu' in d and 'supported_clocks' in d['gpu']:
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import base64
|
|||
import numpy as np
|
||||
import mediapipe as mp
|
||||
from PIL import Image, ImageOps
|
||||
from pi_heif import register_heif_opener
|
||||
from skimage.metrics import structural_similarity as ssim
|
||||
from scipy.stats import beta
|
||||
|
||||
|
|
@ -22,7 +21,7 @@ all_images_by_type = {}
|
|||
|
||||
|
||||
class Result():
|
||||
def __init__(self, typ: str, fn: str, tag: str = None, requested: list = []):
|
||||
def __init__(self, typ: str, fn: str, tag: str | None = None, requested: list = []):
|
||||
self.type = typ
|
||||
self.input = fn
|
||||
self.output = ''
|
||||
|
|
@ -126,11 +125,9 @@ def reset():
|
|||
all_images = []
|
||||
|
||||
|
||||
def upscale_restore_image(res: Result, upscale: bool = False, restore: bool = False):
|
||||
def upscale_restore_image(res: Result, upscale: bool = False):
|
||||
kwargs = util.Map({
|
||||
'image': encode(res.image),
|
||||
'codeformer_visibility': 0.0,
|
||||
'codeformer_weight': 0.0,
|
||||
})
|
||||
if res.image.width >= options.process.target_size and res.image.height >= options.process.target_size:
|
||||
upscale = False
|
||||
|
|
@ -138,25 +135,21 @@ def upscale_restore_image(res: Result, upscale: bool = False, restore: bool = Fa
|
|||
kwargs.upscaler_1 = 'SwinIR_4x'
|
||||
kwargs.upscaling_resize = 2
|
||||
res.ops.append('upscale')
|
||||
if restore:
|
||||
kwargs.codeformer_visibility = 1.0
|
||||
kwargs.codeformer_weight = 0.2
|
||||
res.ops.append('restore')
|
||||
if upscale or restore:
|
||||
if upscale:
|
||||
result = sdapi.postsync('/sdapi/v1/extra-single-image', kwargs)
|
||||
if 'image' not in result:
|
||||
res.message = 'failed to upscale/restore image'
|
||||
res.message = 'failed to upscale image'
|
||||
else:
|
||||
res.image = Image.open(io.BytesIO(base64.b64decode(result['image'])))
|
||||
return res
|
||||
|
||||
|
||||
def interrogate_image(res: Result, tag: str = None):
|
||||
def caption_image(res: Result, tag: str | None = None):
|
||||
caption = ''
|
||||
tags = []
|
||||
for model in options.process.interrogate_model:
|
||||
for model in options.process.caption_model:
|
||||
json = util.Map({ 'image': encode(res.image), 'model': model })
|
||||
result = sdapi.postsync('/sdapi/v1/interrogate', json)
|
||||
result = sdapi.postsync('/sdapi/v1/caption', json)
|
||||
if model == 'clip':
|
||||
caption = result.caption if 'caption' in result else ''
|
||||
caption = caption.split(',')[0].replace(' a ', ' ').strip()
|
||||
|
|
@ -176,7 +169,7 @@ def interrogate_image(res: Result, tag: str = None):
|
|||
tags = tags[:options.process.tag_limit]
|
||||
res.caption = caption
|
||||
res.tags = tags
|
||||
res.ops.append('interrogate')
|
||||
res.ops.append('caption')
|
||||
return res
|
||||
|
||||
|
||||
|
|
@ -267,7 +260,6 @@ def file(filename: str, folder: str, tag = None, requested = []):
|
|||
res = Result(fn = filename, typ='unknown', tag=tag, requested = requested)
|
||||
# open image
|
||||
try:
|
||||
register_heif_opener()
|
||||
res.image = Image.open(filename)
|
||||
if res.image.mode == 'RGBA':
|
||||
res.image = res.image.convert('RGB')
|
||||
|
|
@ -309,13 +301,13 @@ def file(filename: str, folder: str, tag = None, requested = []):
|
|||
if res.image is None:
|
||||
return res
|
||||
# post processing steps
|
||||
res = upscale_restore_image(res, 'upscale' in requested, 'restore' in requested)
|
||||
res = upscale_restore_image(res, 'upscale' in requested)
|
||||
if res.image.width < options.process.target_size or res.image.height < options.process.target_size:
|
||||
res.message = f'low resolution: [{res.image.width}, {res.image.height}]'
|
||||
res.image = None
|
||||
return res
|
||||
if 'interrogate' in requested:
|
||||
res = interrogate_image(res, tag)
|
||||
if 'caption' in requested:
|
||||
res = caption_image(res, tag)
|
||||
if 'resize' in requested:
|
||||
res = resize_image(res)
|
||||
if 'square' in requested:
|
||||
|
|
|
|||
|
|
@ -130,9 +130,9 @@ process = Map({
|
|||
'body_pad': 0.2, # pad body image percentage
|
||||
'body_model': 2, # body model to use 0/low 1/medium 2/high
|
||||
# similarity detection settings
|
||||
# interrogate settings
|
||||
'interrogate': False, # interrogate images
|
||||
'interrogate_model': ['clip', 'deepdanbooru'], # interrogate models
|
||||
# caption settings
|
||||
'caption': False, # caption images
|
||||
'caption_model': ['clip', 'deepdanbooru'], # caption models
|
||||
'tag_limit': 5, # number of tags to extract
|
||||
# validations
|
||||
# tbd
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ def resultsync(req: requests.Response):
|
|||
return res
|
||||
|
||||
|
||||
async def get(endpoint: str, json: dict = None):
|
||||
async def get(endpoint: str, json: dict | None = None):
|
||||
global sess # pylint: disable=global-statement
|
||||
sess = sess if sess is not None else await session()
|
||||
try:
|
||||
|
|
@ -105,7 +105,7 @@ async def get(endpoint: str, json: dict = None):
|
|||
return {}
|
||||
|
||||
|
||||
def getsync(endpoint: str, json: dict = None):
|
||||
def getsync(endpoint: str, json: dict | None = None):
|
||||
try:
|
||||
req = requests.get(f'{sd_url}{endpoint}', json=json, verify=False, auth=authsync()) # pylint: disable=missing-timeout
|
||||
res = resultsync(req)
|
||||
|
|
@ -115,7 +115,7 @@ def getsync(endpoint: str, json: dict = None):
|
|||
return {}
|
||||
|
||||
|
||||
async def post(endpoint: str, json: dict = None):
|
||||
async def post(endpoint: str, json: dict | None = None):
|
||||
global sess # pylint: disable=global-statement
|
||||
# sess = sess if sess is not None else await session()
|
||||
if sess and not sess.closed:
|
||||
|
|
@ -130,7 +130,7 @@ async def post(endpoint: str, json: dict = None):
|
|||
return {}
|
||||
|
||||
|
||||
def postsync(endpoint: str, json: dict = None):
|
||||
def postsync(endpoint: str, json: dict | None = None):
|
||||
req = requests.post(f'{sd_url}{endpoint}', json=json, verify=False, auth=authsync()) # pylint: disable=missing-timeout
|
||||
res = resultsync(req)
|
||||
return res
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
fastapi==0.124.4
|
||||
numpy==2.1.2
|
||||
Pillow==10.4.0
|
||||
|
|
@ -1,11 +1,18 @@
|
|||
{
|
||||
"Google Gemini 2.5 Flash Nano Banana": {
|
||||
"Google Gemini 2.5 Flash Nano Banana": {
|
||||
"path": "gemini-2.5-flash-image",
|
||||
"desc": "Gemini can generate and process images conversationally. You can prompt Gemini with text, images, or a combination of both allowing you to create, edit, and iterate on visuals with unprecedented control.",
|
||||
"preview": "gemini-2.5-flash-image.jpg",
|
||||
"tags": "cloud",
|
||||
"skip": true
|
||||
},
|
||||
"Google Gemini 3.1 Flash Nano Banana": {
|
||||
"path": "gemini-3.1-flash-image-preview",
|
||||
"desc": "Gemini can generate and process images conversationally. You can prompt Gemini with text, images, or a combination of both allowing you to create, edit, and iterate on visuals with unprecedented control.",
|
||||
"preview": "gemini-3.1-flash-image-preview.jpg",
|
||||
"tags": "cloud",
|
||||
"skip": true
|
||||
},
|
||||
"Google Gemini 3.0 Pro Nano Banana": {
|
||||
"path": "gemini-3-pro-image-preview",
|
||||
"desc": "Built on Gemini 3. Create and edit images with studio-quality levels of precision and control",
|
||||
|
|
|
|||
|
|
@ -106,20 +106,22 @@
|
|||
"desc": "Pony V7 is a versatile character generation model based on AuraFlow architecture. It supports a wide range of styles and species types (humanoid, anthro, feral, and more) and handles character interactions through natural language prompts.",
|
||||
"extras": "",
|
||||
"tags": "community",
|
||||
"date": "October September"
|
||||
"date": "2025 October"
|
||||
},
|
||||
"ShuttleAI Shuttle 3.0 Diffusion": {
|
||||
"path": "shuttleai/shuttle-3-diffusion",
|
||||
"desc": "Shuttle uses Flux.1 Schnell as its base. It can produce images similar to Flux Dev or Pro in just 4 steps, and it is licensed under Apache 2. The model was partially de-distilled during training. When used beyond 10 steps, it enters refiner mode enhancing image details without altering the composition",
|
||||
"preview": "shuttleai--shuttle-3-diffusion.jpg",
|
||||
"tags": "community",
|
||||
"date": "2024 November",
|
||||
"skip": true
|
||||
},
|
||||
"ShuttleAI Shuttle 3.1 Aesthetic": {
|
||||
"path": "shuttleai/shuttle-3.1-aesthetic",
|
||||
"desc": "Shuttle uses Flux.1 Schnell as its base. It can produce images similar to Flux Dev or Pro in just 4 steps, and it is licensed under Apache 2. The model was partially de-distilled during training. When used beyond 10 steps, it enters refiner mode enhancing image details without altering the composition",
|
||||
"preview": "shuttleai--shuttle-3_1-aestetic.jpg",
|
||||
"preview": "shuttleai--shuttle-3.1-aesthetic.jpg",
|
||||
"tags": "community",
|
||||
"date": "2024 November",
|
||||
"skip": true
|
||||
},
|
||||
"ShuttleAI Shuttle Jaguar": {
|
||||
|
|
@ -127,13 +129,55 @@
|
|||
"desc": "Shuttle uses Flux.1 Schnell as its base. It can produce images similar to Flux Dev or Pro in just 4 steps, and it is licensed under Apache 2. The model was partially de-distilled during training. When used beyond 10 steps, it enters refiner mode enhancing image details without altering the composition",
|
||||
"preview": "shuttleai--shuttle-jaguar.jpg",
|
||||
"tags": "community",
|
||||
"date": "2025 January",
|
||||
"skip": true
|
||||
},
|
||||
"Anima": {
|
||||
"Anima Preview 1": {
|
||||
"path": "CalamitousFelicitousness/Anima-sdnext-diffusers",
|
||||
"preview": "CalamitousFelicitousness--Anima-sdnext-diffusers.png",
|
||||
"preview": "CalamitousFelicitousness--Anima-sdnext-diffusers.jpg",
|
||||
"desc": "Modified Cosmos-Predict-2B that replaces the T5-11B text encoder with Qwen3-0.6B. Anima is a 2 billion parameter text-to-image model created via a collaboration between CircleStone Labs and Comfy Org. It is focused mainly on anime concepts, characters, and styles, but is also capable of generating a wide variety of other non-photorealistic content. The model is designed for making illustrations and artistic images, and will not work well at realism.",
|
||||
"tags": "community",
|
||||
"date": "2026 January",
|
||||
"skip": true
|
||||
},
|
||||
"Anima Preview 2": {
|
||||
"path": "CalamitousFelicitousness/Anima-Preview-2-sdnext-diffusers",
|
||||
"preview": "CalamitousFelicitousness--Anima-Preview-2-sdnext-diffusers.jpg",
|
||||
"desc": "Anima Preview V2 with improved hyperparameters, extended medium-resolution training for more character knowledge, and a regularization dataset for better natural language comprehension. A 2B parameter anime-focused text-to-image model based on modified Cosmos-Predict-2B with Qwen3-0.6B text encoder.",
|
||||
"tags": "community",
|
||||
"date": "2026 March",
|
||||
"skip": true
|
||||
},
|
||||
"FireRed Image Edit 1.0": {
|
||||
"path": "FireRedTeam/FireRed-Image-Edit-1.0",
|
||||
"preview": "FireRedTeam--FireRed-Image-Edit-1.0.jpg",
|
||||
"desc": "FireRed-Image-Edit is a general-purpose image editing model that delivers high-fidelity and consistent editing across a wide range of scenarios. FireRed is a fine-tune of Qwen-Image-Edit.",
|
||||
"tags": "community",
|
||||
"date": "2026 February",
|
||||
"skip": true
|
||||
},
|
||||
"FireRed Image Edit 1.1": {
|
||||
"path": "FireRedTeam/FireRed-Image-Edit-1.1",
|
||||
"preview": "FireRedTeam--FireRed-Image-Edit-1.1.jpg",
|
||||
"desc": "FireRed-Image-Edit is a general-purpose image editing model that delivers high-fidelity and consistent editing across a wide range of scenarios. FireRed is a fine-tune of Qwen-Image-Edit.",
|
||||
"tags": "community",
|
||||
"date": "2026 February",
|
||||
"skip": true
|
||||
},
|
||||
"Skywork UniPic3": {
|
||||
"path": "Skywork/Unipic3",
|
||||
"preview": "Skywork--Unipic3.jpg",
|
||||
"desc": "UniPic3 is an image editing and multi-image composition model based. It is a fine-tune of Qwen-Image-Edit.",
|
||||
"tags": "community",
|
||||
"date": "2026 February",
|
||||
"skip": true
|
||||
},
|
||||
"Skywork/Unipic3-DMD": {
|
||||
"path": "Skywork/Unipic3-DMD",
|
||||
"preview": "Skywork--Unipic3-DMD.jpg",
|
||||
"desc": "UniPic3-DMD-Model is a few-step image editing and multi-image composition model trained using Distribution Matching Distillation (DMD) and is a fine-tune of Qwen-Image-Edit.",
|
||||
"tags": "community",
|
||||
"date": "2026 February",
|
||||
"skip": true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -170,7 +170,7 @@
|
|||
"tags": "distilled",
|
||||
"extras": "sampler: Default, cfg_scale: 1.0, steps: 4",
|
||||
"size": 8.5,
|
||||
"date": "2025 January"
|
||||
"date": "2026 January"
|
||||
},
|
||||
"Black Forest Labs FLUX.2 Klein 9B": {
|
||||
"path": "black-forest-labs/FLUX.2-klein-9B",
|
||||
|
|
@ -180,6 +180,25 @@
|
|||
"tags": "distilled",
|
||||
"extras": "sampler: Default, cfg_scale: 1.0, steps: 4",
|
||||
"size": 18.5,
|
||||
"date": "2025 January"
|
||||
"date": "2026 January"
|
||||
},
|
||||
"Black Forest Labs FLUX.2 Klein 9B KV": {
|
||||
"path": "black-forest-labs/FLUX.2-klein-9b-kv",
|
||||
"preview": "black-forest-labs--FLUX.2-klein-9b-kv.jpg",
|
||||
"desc": "FLUX.2 klein 9B KV is an optimized variant of FLUX.2 klein 9B with KV-cache support for accelerated multi-reference editing. This variant caches key-value pairs from reference images during the first denoising step, eliminating redundant computation in subsequent steps for significantly faster multi-image editing workflows.",
|
||||
"skip": true,
|
||||
"tags": "distilled",
|
||||
"extras": "sampler: Default, cfg_scale: 1.0, steps: 4",
|
||||
"size": 18.5,
|
||||
"date": "2026 March"
|
||||
},
|
||||
"Meituan LongCat Image-Edit Turbo": {
|
||||
"path": "meituan-longcat/LongCat-Image-Edit-Turbo",
|
||||
"preview": "meituan-longcat--LongCat-Image-Edit.jpg",
|
||||
"desc": "LongCat-Image-Edit-Turbo, the distilled version of LongCat-Image-Edit. It achieves high-quality image editing with only 8 NFEs (Number of Function Evaluations) , offering extremely low inference latency.",
|
||||
"skip": true,
|
||||
"extras": "",
|
||||
"size": 27.30,
|
||||
"date": "2026 February"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,209 @@
|
|||
{
|
||||
"FLUX.1-Dev Nunchaku SVDQuant": {
|
||||
"path": "black-forest-labs/FLUX.1-dev",
|
||||
"subfolder": "nunchaku",
|
||||
"preview": "black-forest-labs--FLUX.1-dev.jpg",
|
||||
"desc": "Nunchaku SVDQuant quantization of FLUX.1-dev transformer with INT4 and SVD rank 32",
|
||||
"skip": true,
|
||||
"nunchaku": ["Model", "TE"],
|
||||
"tags": "nunchaku",
|
||||
"size": 0,
|
||||
"date": "2025 June"
|
||||
},
|
||||
"FLUX.1-Schnell Nunchaku SVDQuant": {
|
||||
"path": "black-forest-labs/FLUX.1-schnell",
|
||||
"subfolder": "nunchaku",
|
||||
"preview": "black-forest-labs--FLUX.1-schnell.jpg",
|
||||
"desc": "Nunchaku SVDQuant quantization of FLUX.1-schnell transformer with INT4 and SVD rank 32",
|
||||
"skip": true,
|
||||
"nunchaku": ["Model", "TE"],
|
||||
"tags": "nunchaku",
|
||||
"extras": "sampler: Default, cfg_scale: 1.0, steps: 4",
|
||||
"size": 0,
|
||||
"date": "2025 June"
|
||||
},
|
||||
"FLUX.1-Kontext Nunchaku SVDQuant": {
|
||||
"path": "black-forest-labs/FLUX.1-Kontext-dev",
|
||||
"subfolder": "nunchaku",
|
||||
"preview": "black-forest-labs--FLUX.1-Kontext-dev.jpg",
|
||||
"desc": "Nunchaku SVDQuant quantization of FLUX.1-Kontext-dev transformer with INT4 and SVD rank 32",
|
||||
"skip": true,
|
||||
"nunchaku": ["Model", "TE"],
|
||||
"tags": "nunchaku",
|
||||
"size": 0,
|
||||
"date": "2025 June"
|
||||
},
|
||||
"FLUX.1-Krea Nunchaku SVDQuant": {
|
||||
"path": "black-forest-labs/FLUX.1-Krea-dev",
|
||||
"subfolder": "nunchaku",
|
||||
"preview": "black-forest-labs--FLUX.1-Krea-dev.jpg",
|
||||
"desc": "Nunchaku SVDQuant quantization of FLUX.1-Krea-dev transformer with INT4 and SVD rank 32",
|
||||
"skip": true,
|
||||
"nunchaku": ["Model", "TE"],
|
||||
"tags": "nunchaku",
|
||||
"size": 0,
|
||||
"date": "2025 June"
|
||||
},
|
||||
"FLUX.1-Fill Nunchaku SVDQuant": {
|
||||
"path": "black-forest-labs/FLUX.1-Fill-dev",
|
||||
"subfolder": "nunchaku",
|
||||
"preview": "black-forest-labs--FLUX.1-Fill-dev.jpg",
|
||||
"desc": "Nunchaku SVDQuant quantization of FLUX.1-Fill-dev transformer for inpainting",
|
||||
"skip": true,
|
||||
"hidden": true,
|
||||
"nunchaku": ["Model", "TE"],
|
||||
"tags": "nunchaku",
|
||||
"size": 0,
|
||||
"date": "2025 June"
|
||||
},
|
||||
"FLUX.1-Depth Nunchaku SVDQuant": {
|
||||
"path": "black-forest-labs/FLUX.1-Depth-dev",
|
||||
"subfolder": "nunchaku",
|
||||
"preview": "black-forest-labs--FLUX.1-Depth-dev.jpg",
|
||||
"desc": "Nunchaku SVDQuant quantization of FLUX.1-Depth-dev transformer for depth-conditioned generation",
|
||||
"skip": true,
|
||||
"hidden": true,
|
||||
"nunchaku": ["Model", "TE"],
|
||||
"tags": "nunchaku",
|
||||
"size": 0,
|
||||
"date": "2025 June"
|
||||
},
|
||||
"Shuttle Jaguar Nunchaku SVDQuant": {
|
||||
"path": "shuttleai/shuttle-jaguar",
|
||||
"subfolder": "nunchaku",
|
||||
"preview": "shuttleai--shuttle-jaguar.jpg",
|
||||
"desc": "Nunchaku SVDQuant quantization of Shuttle Jaguar transformer",
|
||||
"skip": true,
|
||||
"nunchaku": ["Model", "TE"],
|
||||
"tags": "nunchaku",
|
||||
"size": 0,
|
||||
"date": "2025 June"
|
||||
},
|
||||
"Qwen-Image Nunchaku SVDQuant": {
|
||||
"path": "Qwen/Qwen-Image",
|
||||
"subfolder": "nunchaku",
|
||||
"preview": "Qwen--Qwen-Image.jpg",
|
||||
"desc": "Nunchaku SVDQuant quantization of Qwen-Image transformer with INT4 and SVD rank 128",
|
||||
"skip": true,
|
||||
"nunchaku": ["Model"],
|
||||
"tags": "nunchaku",
|
||||
"size": 0,
|
||||
"date": "2025 June"
|
||||
},
|
||||
"Qwen-Lightning (8-step) Nunchaku SVDQuant": {
|
||||
"path": "vladmandic/Qwen-Lightning",
|
||||
"subfolder": "nunchaku",
|
||||
"preview": "vladmandic--Qwen-Lightning.jpg",
|
||||
"desc": "Nunchaku SVDQuant quantization of Qwen-Lightning (8-step distilled) transformer with INT4 and SVD rank 128",
|
||||
"skip": true,
|
||||
"nunchaku": ["Model"],
|
||||
"tags": "nunchaku",
|
||||
"extras": "steps: 8",
|
||||
"size": 0,
|
||||
"date": "2025 June"
|
||||
},
|
||||
"Qwen-Lightning (4-step) Nunchaku SVDQuant": {
|
||||
"path": "vladmandic/Qwen-Lightning",
|
||||
"subfolder": "nunchaku-4step",
|
||||
"preview": "vladmandic--Qwen-Lightning.jpg",
|
||||
"desc": "Nunchaku SVDQuant quantization of Qwen-Lightning (4-step distilled) transformer with INT4 and SVD rank 128",
|
||||
"skip": true,
|
||||
"nunchaku": ["Model"],
|
||||
"tags": "nunchaku",
|
||||
"extras": "steps: 4",
|
||||
"size": 0,
|
||||
"date": "2025 June"
|
||||
},
|
||||
"Qwen-Image-Edit Nunchaku SVDQuant": {
|
||||
"path": "Qwen/Qwen-Image-Edit",
|
||||
"subfolder": "nunchaku",
|
||||
"preview": "Qwen--Qwen-Image-Edit.jpg",
|
||||
"desc": "Nunchaku SVDQuant quantization of Qwen-Image-Edit transformer with INT4 and SVD rank 128",
|
||||
"skip": true,
|
||||
"nunchaku": ["Model"],
|
||||
"tags": "nunchaku",
|
||||
"size": 0,
|
||||
"date": "2025 June"
|
||||
},
|
||||
"Qwen-Lightning-Edit (8-step) Nunchaku SVDQuant": {
|
||||
"path": "vladmandic/Qwen-Lightning-Edit",
|
||||
"subfolder": "nunchaku",
|
||||
"preview": "vladmandic--Qwen-Lightning-Edit.jpg",
|
||||
"desc": "Nunchaku SVDQuant quantization of Qwen-Lightning-Edit (8-step distilled editing) transformer with INT4 and SVD rank 128",
|
||||
"skip": true,
|
||||
"nunchaku": ["Model"],
|
||||
"tags": "nunchaku",
|
||||
"extras": "steps: 8",
|
||||
"size": 0,
|
||||
"date": "2025 June"
|
||||
},
|
||||
"Qwen-Lightning-Edit (4-step) Nunchaku SVDQuant": {
|
||||
"path": "vladmandic/Qwen-Lightning-Edit",
|
||||
"subfolder": "nunchaku-4step",
|
||||
"preview": "vladmandic--Qwen-Lightning-Edit.jpg",
|
||||
"desc": "Nunchaku SVDQuant quantization of Qwen-Lightning-Edit (4-step distilled editing) transformer with INT4 and SVD rank 128",
|
||||
"skip": true,
|
||||
"nunchaku": ["Model"],
|
||||
"tags": "nunchaku",
|
||||
"extras": "steps: 4",
|
||||
"size": 0,
|
||||
"date": "2025 June"
|
||||
},
|
||||
"Qwen-Image-Edit-2509 Nunchaku SVDQuant": {
|
||||
"path": "Qwen/Qwen-Image-Edit-2509",
|
||||
"subfolder": "nunchaku",
|
||||
"preview": "Qwen--Qwen-Image-Edit-2509.jpg",
|
||||
"desc": "Nunchaku SVDQuant quantization of Qwen-Image-Edit-2509 transformer with INT4 and SVD rank 128",
|
||||
"skip": true,
|
||||
"nunchaku": ["Model"],
|
||||
"tags": "nunchaku",
|
||||
"size": 0,
|
||||
"date": "2025 September"
|
||||
},
|
||||
"Sana 1.6B 1k Nunchaku SVDQuant": {
|
||||
"path": "Efficient-Large-Model/Sana_1600M_1024px_BF16_diffusers",
|
||||
"subfolder": "nunchaku",
|
||||
"preview": "Efficient-Large-Model--Sana_1600M_1024px_BF16_diffusers.jpg",
|
||||
"desc": "Nunchaku SVDQuant quantization of Sana 1.6B 1024px transformer with INT4 and SVD rank 32",
|
||||
"skip": true,
|
||||
"nunchaku": ["Model"],
|
||||
"tags": "nunchaku",
|
||||
"size": 0,
|
||||
"date": "2025 June"
|
||||
},
|
||||
"Z-Image-Turbo Nunchaku SVDQuant": {
|
||||
"path": "Tongyi-MAI/Z-Image-Turbo",
|
||||
"subfolder": "nunchaku",
|
||||
"preview": "Tongyi-MAI--Z-Image-Turbo.jpg",
|
||||
"desc": "Nunchaku SVDQuant quantization of Z-Image-Turbo transformer with INT4 and SVD rank 128",
|
||||
"skip": true,
|
||||
"nunchaku": ["Model"],
|
||||
"tags": "nunchaku",
|
||||
"extras": "sampler: Default, cfg_scale: 1.0, steps: 9",
|
||||
"size": 0,
|
||||
"date": "2025 June"
|
||||
},
|
||||
"SDXL Base Nunchaku SVDQuant": {
|
||||
"path": "stabilityai/stable-diffusion-xl-base-1.0",
|
||||
"subfolder": "nunchaku",
|
||||
"preview": "stabilityai--stable-diffusion-xl-base-1.0.jpg",
|
||||
"desc": "Nunchaku SVDQuant quantization of SDXL Base 1.0 UNet with INT4 and SVD rank 32",
|
||||
"skip": true,
|
||||
"nunchaku": ["Model"],
|
||||
"tags": "nunchaku",
|
||||
"size": 0,
|
||||
"date": "2025 June"
|
||||
},
|
||||
"SDXL Turbo Nunchaku SVDQuant": {
|
||||
"path": "stabilityai/sdxl-turbo",
|
||||
"subfolder": "nunchaku",
|
||||
"preview": "stabilityai--sdxl-turbo.jpg",
|
||||
"desc": "Nunchaku SVDQuant quantization of SDXL Turbo UNet with INT4 and SVD rank 32",
|
||||
"skip": true,
|
||||
"nunchaku": ["Model"],
|
||||
"tags": "nunchaku",
|
||||
"extras": "sampler: Default, cfg_scale: 1.0, steps: 4",
|
||||
"size": 0,
|
||||
"date": "2025 June"
|
||||
}
|
||||
}
|
||||
|
|
@ -25,11 +25,13 @@ const jsConfig = defineConfig([
|
|||
ecmaVersion: 'latest',
|
||||
},
|
||||
globals: { // Set per project
|
||||
...globals.node,
|
||||
...globals.builtin,
|
||||
...globals.browser,
|
||||
...globals.jquery,
|
||||
panzoom: 'readonly',
|
||||
authFetch: 'readonly',
|
||||
initServerInfo: 'readonly',
|
||||
log: 'readonly',
|
||||
debug: 'readonly',
|
||||
error: 'readonly',
|
||||
|
|
@ -93,6 +95,7 @@ const jsConfig = defineConfig([
|
|||
initGPU: 'readonly',
|
||||
startGPU: 'readonly',
|
||||
disableNVML: 'readonly',
|
||||
hash: 'readonly',
|
||||
idbGet: 'readonly',
|
||||
idbPut: 'readonly',
|
||||
idbDel: 'readonly',
|
||||
|
|
@ -127,6 +130,7 @@ const jsConfig = defineConfig([
|
|||
camelcase: 'off',
|
||||
'default-case': 'off',
|
||||
'max-classes-per-file': 'warn',
|
||||
'guard-for-in': 'off',
|
||||
'no-await-in-loop': 'off',
|
||||
'no-bitwise': 'off',
|
||||
'no-continue': 'off',
|
||||
|
|
@ -144,6 +148,7 @@ const jsConfig = defineConfig([
|
|||
'prefer-rest-params': 'off',
|
||||
'prefer-template': 'warn',
|
||||
'promise/no-nesting': 'off',
|
||||
'@typescript-eslint/no-for-in-array': 'off',
|
||||
radix: 'off',
|
||||
'@stylistic/brace-style': [
|
||||
'error',
|
||||
|
|
@ -333,6 +338,7 @@ export default defineConfig([
|
|||
'**/exifr.js',
|
||||
'**/jquery.js',
|
||||
'**/sparkline.js',
|
||||
'**/sha256.js',
|
||||
'**/iframeResizer.min.js',
|
||||
]),
|
||||
...jsConfig,
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 2a7005fbcf8985644b66121365fa7228a65f34b0
|
||||
Subproject commit d4eab2166e4d9b52e42924cc942198f9e22eb916
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit ddf821483c8bcdac4868f15a9a838b45b4dd30ad
|
||||
Subproject commit 006f08f499bbe69c484f0f1cc332bbf0e75526c2
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit 79cae1944646e57cfbfb126a971a04e44e45d776
|
||||
Subproject commit 1e840033b040d8915ddfb5dbf62c80f411bcec0a
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit 357985697de0a457e401bccbc41132322648d0ba
|
||||
Subproject commit c7af727f31758c9fc96cf0429bcf3608858a15e8
|
||||
|
|
@ -1 +0,0 @@
|
|||
Subproject commit 3ec8f9eb796be519a98de985bac03645d0040ede
|
||||
|
|
@ -4,46 +4,6 @@
|
|||
#licenses pre { margin: 1em 0 2em 0;}
|
||||
</style>
|
||||
|
||||
<h2><a href="https://github.com/sczhou/CodeFormer/blob/master/LICENSE">CodeFormer</a></h2>
|
||||
<small>Parts of CodeFormer code had to be copied to be compatible with GFPGAN.</small>
|
||||
<pre>
|
||||
S-Lab License 1.0
|
||||
|
||||
Copyright 2022 S-Lab
|
||||
|
||||
Redistribution and use for non-commercial purpose in source and
|
||||
binary forms, with or without modification, are permitted provided
|
||||
that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
In the event that redistribution and/or use for commercial purpose in
|
||||
source or binary forms, with or without modification is required,
|
||||
please contact the contributor(s) of the work.
|
||||
</pre>
|
||||
|
||||
|
||||
<h2><a href="https://github.com/victorca25/iNNfer/blob/main/LICENSE">ESRGAN</a></h2>
|
||||
<small>Code for architecture and reading models copied.</small>
|
||||
|
|
|
|||
16693
html/locale_de.json
3033
html/locale_en.json
16625
html/locale_es.json
16737
html/locale_fr.json
16612
html/locale_hr.json
15121
html/locale_it.json
16734
html/locale_ja.json
19049
html/locale_ko.json
16711
html/locale_pt.json
16795
html/locale_ru.json
17123
html/locale_zh.json
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:svg="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
||||
<!-- Generator: Adobe Illustrator 30.2.0, SVG Export Plug-In . SVG Version: 2.1.1 Build 105) -->
|
||||
<path d="M333.62,470.26l-9.4,4.63-39.05-21.52,16.16,32.8-9.4,4.63-24.4-49.52,9.4-4.63,39.09,21.59-16.19-32.87,9.4-4.63,24.4,49.52h-.01ZM442.86,365.07h0ZM334.71,418.37l6.05,12.27h.01l22.32-11.01,4.03,8.18-22.32,11,6.26,12.7,38.07-18.76,3.55-23.51-18.59-10.28-39.38,19.41h0ZM457.12,338.1l-205,101,31.64,65.17,205.12-100.53-31.76-65.64h0ZM423.12,426.16l-21.57-12.47-2.75,24.46-11.34,5.59h0l-41.79,20.59-24.4-49.52,43.06-21.21.19.07-.16-.09h-.01.01l11.54-5.68,19.04,10.95,2.2-21.42,11.34-5.59-4.76,31.42,30.96,17.21-11.55,5.69h-.01ZM433.46,369.7l-14.85,7.32-4.03-8.18,39.11-19.27,4.03,8.18-14.86,7.32,20.37,41.34-9.4,4.63-20.37-41.34h0Z" fill="#231f20"/>
|
||||
<polygon points="248.11 427.73 453.11 326.73 453.11 113.73 248.11 7.73 248.11 117.73 333.11 167.73 333.11 267.73 248.11 317.73 248.11 427.73" fill="#231f20"/>
|
||||
<polygon points="23.11 150.73 23.11 275.73 103.11 319.73 103.11 331.73 23.11 375.73 23.11 500.73 228.11 386.73 228.11 261.73 143.11 221.73 143.11 209.73 228.11 161.73 228.11 36.73 23.11 150.73" fill="#231f20"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:svg="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
||||
<!-- Generator: Adobe Illustrator 30.2.0, SVG Export Plug-In . SVG Version: 2.1.1 Build 105) -->
|
||||
<path d="M333.62,470.26l-9.4,4.63-39.05-21.52,16.16,32.8-9.4,4.63-24.4-49.52,9.4-4.63,39.09,21.59-16.19-32.87,9.4-4.63,24.4,49.52h-.01ZM442.86,365.07h0ZM334.71,418.37l6.05,12.27h.01l22.32-11.01,4.03,8.18-22.32,11,6.26,12.7,38.07-18.76,3.55-23.51-18.59-10.28-39.38,19.41h0ZM457.12,338.1l-205,101,31.64,65.17,205.12-100.53-31.76-65.64h0ZM423.12,426.16l-21.57-12.47-2.75,24.46-11.34,5.59h0l-41.79,20.59-24.4-49.52,43.06-21.21.19.07-.16-.09h-.01.01l11.54-5.68,19.04,10.95,2.2-21.42,11.34-5.59-4.76,31.42,30.96,17.21-11.55,5.69h-.01ZM433.46,369.7l-14.85,7.32-4.03-8.18,39.11-19.27,4.03,8.18-14.86,7.32,20.37,41.34-9.4,4.63-20.37-41.34h0Z" fill="#fff"/>
|
||||
<polygon points="248.11 427.73 453.11 326.73 453.11 113.73 248.11 7.73 248.11 117.73 333.11 167.73 333.11 267.73 248.11 317.73 248.11 427.73" fill="#fff"/>
|
||||
<polygon points="23.11 150.73 23.11 275.73 103.11 319.73 103.11 331.73 23.11 375.73 23.11 500.73 228.11 386.73 228.11 261.73 143.11 221.73 143.11 209.73 228.11 161.73 228.11 36.73 23.11 150.73" fill="#fff"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 473 KiB |
BIN
html/logo-wm.png
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 337 KiB |
|
Before Width: | Height: | Size: 353 KiB |
1063
installer.py
|
|
@ -24,9 +24,9 @@ async function authFetch(url, options = {}) {
|
|||
let res;
|
||||
try {
|
||||
res = await fetch(url, options);
|
||||
if (!res.ok) error('fetch', { status: res.status, url, user, token });
|
||||
if (!res.ok) error('fetch', { status: res?.status || 503, url, user, token });
|
||||
} catch (err) {
|
||||
error('fetch', { status: res.status, url, user, token, error: err });
|
||||
error('fetch', { status: res?.status || 503, url, user, token, error: err });
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
String.prototype.format = function (args) { // eslint-disable-line no-extend-native, func-names
|
||||
let thisString = '';
|
||||
for (let charPos = 0; charPos < this.length; charPos++) thisString += this[charPos];
|
||||
for (const key in args) { // eslint-disable-line guard-for-in
|
||||
for (const key in args) {
|
||||
const stringKey = `{${key}}`;
|
||||
thisString = thisString.replace(new RegExp(stringKey, 'g'), args[key]);
|
||||
}
|
||||
|
|
@ -11,6 +11,9 @@ String.prototype.format = function (args) { // eslint-disable-line no-extend-nat
|
|||
let selectedURL = '';
|
||||
let selectedName = '';
|
||||
let selectedType = '';
|
||||
let selectedBase = '';
|
||||
let selectedModelId = '';
|
||||
let selectedVersionId = '';
|
||||
|
||||
function clearModelDetails() {
|
||||
const el = gradioApp().getElementById('model-details') || gradioApp().getElementById('civitai_models_output') || gradioApp().getElementById('models_outcome');
|
||||
|
|
@ -84,7 +87,7 @@ async function modelCardClick(id) {
|
|||
data = data[0]; // assuming the first item is the one we want
|
||||
|
||||
const versionsHTML = data.versions.map((v) => modelVersionsHTML.format({
|
||||
url: `<div class="link" onclick="startCivitDownload('${v.files[0]?.url}', '${v.files[0]?.name}', '${data.type}')"> \udb80\uddda </div>`,
|
||||
url: `<div class="link" onclick="startCivitDownload('${v.files[0]?.url}', '${v.files[0]?.name}', '${data.type}', '${v.base || ''}', ${data.id}, ${v.id})"> \udb80\uddda </div>`,
|
||||
name: v.name || 'unknown',
|
||||
type: v.files[0]?.type || 'unknown',
|
||||
base: v.base || 'unknown',
|
||||
|
|
@ -113,11 +116,14 @@ async function modelCardClick(id) {
|
|||
el.innerHTML = modelHTML;
|
||||
}
|
||||
|
||||
function startCivitDownload(url, name, type) {
|
||||
log('startCivitDownload', { url, name, type });
|
||||
function startCivitDownload(url, name, type, base, modelId, versionId) {
|
||||
log('startCivitDownload', { url, name, type, base, modelId, versionId });
|
||||
selectedURL = [url];
|
||||
selectedName = [name];
|
||||
selectedType = [type];
|
||||
selectedBase = [base || ''];
|
||||
selectedModelId = [modelId || 0];
|
||||
selectedVersionId = [versionId || 0];
|
||||
const civitDownloadBtn = gradioApp().getElementById('civitai_download_btn');
|
||||
if (civitDownloadBtn) civitDownloadBtn.click();
|
||||
}
|
||||
|
|
@ -128,20 +134,44 @@ function startCivitAllDownload(evt) {
|
|||
selectedURL = [];
|
||||
selectedName = [];
|
||||
selectedType = [];
|
||||
selectedBase = [];
|
||||
selectedModelId = [];
|
||||
selectedVersionId = [];
|
||||
for (const version of versions) {
|
||||
const parsed = version.querySelector('td:nth-child(1) div')?.getAttribute('onclick')?.match(/startCivitDownload\('([^']+)', '([^']+)', '([^']+)'\)/);
|
||||
if (!parsed || parsed.length < 4) continue;
|
||||
const parsed = version.querySelector('td:nth-child(1) div')?.getAttribute('onclick')?.match(/startCivitDownload\('([^']+)', '([^']+)', '([^']+)', '([^']*)', (\d+), (\d+)\)/);
|
||||
if (!parsed || parsed.length < 7) continue;
|
||||
selectedURL.push(parsed[1]);
|
||||
selectedName.push(parsed[2]);
|
||||
selectedType.push(parsed[3]);
|
||||
selectedBase.push(parsed[4]);
|
||||
selectedModelId.push(parseInt(parsed[5], 10));
|
||||
selectedVersionId.push(parseInt(parsed[6], 10));
|
||||
}
|
||||
const civitDownloadBtn = gradioApp().getElementById('civitai_download_btn');
|
||||
if (civitDownloadBtn) civitDownloadBtn.click();
|
||||
}
|
||||
|
||||
function downloadCivitModel(modelUrl, modelName, modelType, modelPath, civitToken, innerHTML) {
|
||||
log('downloadCivitModel', { modelUrl, modelName, modelType, modelPath, civitToken });
|
||||
function downloadCivitModel(modelUrl, modelName, modelType, modelBase, mId, vId, modelPath, civitToken, innerHTML) {
|
||||
log('downloadCivitModel', { modelUrl, modelName, modelType, modelBase, mId, vId, modelPath, civitToken });
|
||||
const el = gradioApp().getElementById('civitai_models_output') || gradioApp().getElementById('models_outcome');
|
||||
const currentHTML = el?.innerHTML || '';
|
||||
return [selectedURL, selectedName, selectedType, modelPath, civitToken, currentHTML];
|
||||
return [selectedURL, selectedName, selectedType, selectedBase, selectedModelId, selectedVersionId, modelPath, civitToken, currentHTML];
|
||||
}
|
||||
|
||||
let civitMutualExcludeBound = false;
|
||||
|
||||
function civitaiMutualExclude() {
|
||||
if (civitMutualExcludeBound) return;
|
||||
const searchEl = gradioApp().querySelector('#civit_search_text textarea');
|
||||
const tagEl = gradioApp().querySelector('#civit_search_tag textarea');
|
||||
if (!searchEl || !tagEl) return;
|
||||
civitMutualExcludeBound = true;
|
||||
searchEl.addEventListener('input', () => {
|
||||
tagEl.closest('.gradio-textbox')?.classList.toggle('disabled-look', !!searchEl.value.trim());
|
||||
});
|
||||
tagEl.addEventListener('input', () => {
|
||||
searchEl.closest('.gradio-textbox')?.classList.toggle('disabled-look', !!tagEl.value.trim());
|
||||
});
|
||||
}
|
||||
|
||||
onUiLoaded(civitaiMutualExclude);
|
||||
|
|
|
|||
|
|
@ -171,6 +171,12 @@ async function filterExtraNetworksForTab(searchTerm) {
|
|||
.toLowerCase()
|
||||
.includes('quantized') ? '' : 'none';
|
||||
});
|
||||
} else if (searchTerm === 'nunchaku/') {
|
||||
cards.forEach((elem) => {
|
||||
elem.style.display = elem.dataset.tags
|
||||
.toLowerCase()
|
||||
.includes('nunchaku') ? '' : 'none';
|
||||
});
|
||||
} else if (searchTerm === 'local/') {
|
||||
cards.forEach((elem) => {
|
||||
elem.style.display = elem.dataset.name
|
||||
|
|
|
|||
|
|
@ -5,10 +5,7 @@ let currentImage = null;
|
|||
let currentGalleryFolder = null;
|
||||
let pruneImagesTimer;
|
||||
let outstanding = 0;
|
||||
let lastSort = 0;
|
||||
let lastSortName = 'None';
|
||||
let gallerySelection = { files: [], index: -1 };
|
||||
const galleryHashes = new Set();
|
||||
let maintenanceController = new AbortController();
|
||||
const folderStylesheet = new CSSStyleSheet();
|
||||
const fileStylesheet = new CSSStyleSheet();
|
||||
|
|
@ -26,6 +23,31 @@ const el = {
|
|||
|
||||
const SUPPORTED_EXTENSIONS = ['jpg', 'jpeg', 'png', 'webp', 'tiff', 'jp2', 'jxl', 'gif', 'mp4', 'mkv', 'avi', 'mjpeg', 'mpg', 'avr'];
|
||||
|
||||
const gallerySorter = {
|
||||
nameA: { name: 'Name Ascending', func: (a, b) => a.name.localeCompare(b.name) },
|
||||
nameD: { name: 'Name Descending', func: (b, a) => a.name.localeCompare(b.name) },
|
||||
sizeA: { name: 'Size Ascending', func: (a, b) => a.size - b.size },
|
||||
sizeD: { name: 'Size Descending', func: (b, a) => a.size - b.size },
|
||||
resA: { name: 'Resolution Ascending', func: (a, b) => a.width * a.height - b.width * b.height },
|
||||
resD: { name: 'Resolution Descending', func: (b, a) => a.width * a.height - b.width * b.height },
|
||||
modA: { name: 'Modified Ascending', func: (a, b) => a.mtime - b.mtime },
|
||||
modD: { name: 'Modified Descending', func: (b, a) => a.mtime - b.mtime },
|
||||
none: { name: 'None', func: undefined },
|
||||
};
|
||||
|
||||
let sortMode = gallerySorter.none;
|
||||
|
||||
async function getHash(str) {
|
||||
let hex = '';
|
||||
const strBuf = new TextEncoder().encode(str);
|
||||
let hashBuf;
|
||||
if (crypto?.subtle?.digest) hashBuf = await crypto.subtle.digest('SHA-256', strBuf);
|
||||
else hashBuf = hash(strBuf).buffer; // from sha256.js
|
||||
const view = new DataView(hashBuf);
|
||||
for (let i = 0; i < hashBuf.byteLength; i += 4) hex += (`00000000${view.getUint32(i).toString(16)}`).slice(-8);
|
||||
return hex;
|
||||
}
|
||||
|
||||
function getVisibleGalleryFiles() {
|
||||
if (!el.files) return [];
|
||||
return Array.from(el.files.children).filter((node) => node.name && node.offsetParent);
|
||||
|
|
@ -100,7 +122,8 @@ async function awaitForOutstanding(num, signal) {
|
|||
* @param {AbortSignal} signal - AbortController signal
|
||||
*/
|
||||
async function awaitForGallery(expectedSize, signal) {
|
||||
while (galleryHashes.size < expectedSize && !signal.aborted) await new Promise((resolve) => { setTimeout(resolve, 500); }); // longer interval because it's a low priority check
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
while (Math.max(galleryHashes.size, galleryHashes.fallback) < expectedSize && !signal.aborted) await new Promise((resolve) => { setTimeout(resolve, 500); }); // longer interval because it's a low priority check
|
||||
signal.throwIfAborted();
|
||||
}
|
||||
|
||||
|
|
@ -174,6 +197,25 @@ function updateGalleryStyles() {
|
|||
|
||||
// Classes
|
||||
|
||||
class HashSet extends Set {
|
||||
constructor(val) {
|
||||
super(val);
|
||||
this.fallback = 0;
|
||||
}
|
||||
|
||||
add(value) {
|
||||
++this.fallback;
|
||||
super.add(value);
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.fallback = 0;
|
||||
super.clear();
|
||||
}
|
||||
}
|
||||
|
||||
const galleryHashes = new HashSet();
|
||||
|
||||
class SimpleProgressBar {
|
||||
#container = document.createElement('div');
|
||||
#progress = document.createElement('div');
|
||||
|
|
@ -432,7 +474,11 @@ class GalleryFile extends HTMLElement {
|
|||
}
|
||||
}
|
||||
|
||||
this.hash = await getHash(`${this.src}/${this.size}/${this.mtime}`); // eslint-disable-line no-use-before-define
|
||||
this.hash = await getHash(`${this.src}/${this.size}/${this.mtime}`)
|
||||
.catch((err) => {
|
||||
error('getHash:', err);
|
||||
return null;
|
||||
});
|
||||
const cachedData = (this.hash && opts.browser_cache) ? await idbGet(this.hash).catch(() => undefined) : undefined;
|
||||
const img = document.createElement('img');
|
||||
img.className = 'gallery-file';
|
||||
|
|
@ -467,7 +513,7 @@ class GalleryFile extends HTMLElement {
|
|||
this.height = json.height;
|
||||
this.size = json.size;
|
||||
this.mtime = new Date(json.mtime);
|
||||
if (opts.browser_cache) {
|
||||
if (opts.browser_cache && this.hash) {
|
||||
await idbAdd({
|
||||
hash: this.hash,
|
||||
folder: this.fullFolder,
|
||||
|
|
@ -642,19 +688,6 @@ async function addSeparators() {
|
|||
|
||||
const gallerySendImage = (_images) => [currentImage]; // invoked by gradio button
|
||||
|
||||
async function getHash(str, algo = 'SHA-256') {
|
||||
try {
|
||||
let hex = '';
|
||||
const strBuf = new TextEncoder().encode(str);
|
||||
const hash = await crypto.subtle.digest(algo, strBuf);
|
||||
const view = new DataView(hash);
|
||||
for (let i = 0; i < hash.byteLength; i += 4) hex += (`00000000${view.getUint32(i).toString(16)}`).slice(-8);
|
||||
return hex;
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to update status with sort mode
|
||||
* @param {...string|[string, string]} messages - Each can be either a string to use as-is, or an array of a string label and value
|
||||
|
|
@ -662,7 +695,7 @@ async function getHash(str, algo = 'SHA-256') {
|
|||
*/
|
||||
function updateStatusWithSort(...messages) {
|
||||
if (!el.status) return;
|
||||
messages.unshift(['Sort', lastSortName]);
|
||||
messages.unshift(['Sort', sortMode.name]);
|
||||
const fragment = document.createDocumentFragment();
|
||||
for (let i = 0; i < messages.length; i++) {
|
||||
const div = document.createElement('div');
|
||||
|
|
@ -837,11 +870,14 @@ const findDuplicates = (arr, key) => {
|
|||
});
|
||||
};
|
||||
|
||||
async function gallerySort(btn) {
|
||||
async function gallerySort(key) {
|
||||
if (!Object.hasOwn(gallerySorter, key)) {
|
||||
error(`Gallery: "${key}" is not a valid gallery sorting key`);
|
||||
return;
|
||||
}
|
||||
const t0 = performance.now();
|
||||
const arr = Array.from(el.files.children).filter((node) => node.name); // filter out separators
|
||||
if (arr.length === 0) return; // no files to sort
|
||||
if (btn) lastSort = btn.charCodeAt(0);
|
||||
const fragment = document.createDocumentFragment();
|
||||
|
||||
// Helper to get directory path from a file node
|
||||
|
|
@ -864,60 +900,17 @@ async function gallerySort(btn) {
|
|||
folderGroups.get(dir).push(file);
|
||||
}
|
||||
|
||||
// Sort function based on current sort mode
|
||||
let sortFn;
|
||||
switch (lastSort) {
|
||||
case 61789: // name asc
|
||||
lastSortName = 'Name Ascending';
|
||||
sortFn = (a, b) => a.name.localeCompare(b.name);
|
||||
break;
|
||||
case 61790: // name dsc
|
||||
lastSortName = 'Name Descending';
|
||||
sortFn = (a, b) => b.name.localeCompare(a.name);
|
||||
break;
|
||||
case 61792: // size asc
|
||||
lastSortName = 'Size Ascending';
|
||||
sortFn = (a, b) => a.size - b.size;
|
||||
break;
|
||||
case 61793: // size dsc
|
||||
lastSortName = 'Size Descending';
|
||||
sortFn = (a, b) => b.size - a.size;
|
||||
break;
|
||||
case 61794: // resolution asc
|
||||
lastSortName = 'Resolution Ascending';
|
||||
sortFn = (a, b) => a.width * a.height - b.width * b.height;
|
||||
break;
|
||||
case 61795: // resolution dsc
|
||||
lastSortName = 'Resolution Descending';
|
||||
sortFn = (a, b) => b.width * b.height - a.width * a.height;
|
||||
break;
|
||||
case 61662:
|
||||
lastSortName = 'Modified Ascending';
|
||||
sortFn = (a, b) => a.mtime - b.mtime;
|
||||
break;
|
||||
case 61661:
|
||||
lastSortName = 'Modified Descending';
|
||||
sortFn = (a, b) => b.mtime - a.mtime;
|
||||
break;
|
||||
default:
|
||||
lastSortName = 'None';
|
||||
sortFn = null;
|
||||
break;
|
||||
}
|
||||
sortMode = gallerySorter[key];
|
||||
|
||||
// Sort root files
|
||||
if (sortFn) {
|
||||
rootFiles.sort(sortFn);
|
||||
}
|
||||
rootFiles.sort(sortMode.func);
|
||||
rootFiles.forEach((node) => fragment.appendChild(node));
|
||||
|
||||
// Sort folder names alphabetically, then sort files within each folder
|
||||
const sortedFolderNames = Array.from(folderGroups.keys()).sort((a, b) => a.localeCompare(b));
|
||||
for (const folderName of sortedFolderNames) {
|
||||
const files = folderGroups.get(folderName);
|
||||
if (sortFn) {
|
||||
files.sort(sortFn);
|
||||
}
|
||||
files.sort(sortMode.func);
|
||||
files.forEach((node) => fragment.appendChild(node));
|
||||
}
|
||||
|
||||
|
|
@ -942,7 +935,7 @@ async function gallerySort(btn) {
|
|||
}
|
||||
|
||||
const t1 = performance.now();
|
||||
log(`gallerySort: char=${lastSort} len=${arr.length} time=${Math.floor(t1 - t0)} sort=${lastSortName}`);
|
||||
log(`gallerySort: sort=${sortMode.name} len=${arr.length} time=${Math.floor(t1 - t0)}`);
|
||||
updateStatusWithSort(['Images', arr.length.toLocaleString()], `${iconStopwatch} ${Math.floor(t1 - t0).toLocaleString()}ms`);
|
||||
refreshGallerySelection();
|
||||
}
|
||||
|
|
@ -1353,6 +1346,7 @@ async function initGallery() { // triggered on gradio change to monitor when ui
|
|||
|
||||
monitorGalleries();
|
||||
updateFolders();
|
||||
initGalleryAutoRefresh();
|
||||
[
|
||||
'browser_folders',
|
||||
'outdir_samples',
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ async function updateGPU() {
|
|||
const gpuEl = document.getElementById('gpu');
|
||||
const gpuTable = document.getElementById('gpu-table');
|
||||
try {
|
||||
const res = await authFetch(`${window.api}/gpu`);
|
||||
const res = await authFetch(`${window.api}/gpu-smi`);
|
||||
if (!res.ok) {
|
||||
clearInterval(gpuInterval);
|
||||
gpuEl.style.display = 'none';
|
||||
|
|
|
|||
|
|
@ -82,7 +82,6 @@ async function logMonitor() {
|
|||
for (const line of lines) addLogLine(line);
|
||||
if (!logConnected) {
|
||||
logConnected = true;
|
||||
monitorConnection();
|
||||
xhrPost(`${window.api}/log`, { debug: 'connected' });
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||