From 89dd406a1d0287f9b4150203c7819d37d3a60314 Mon Sep 17 00:00:00 2001 From: awsr <43862868+awsr@users.noreply.github.com> Date: Sat, 7 Feb 2026 12:14:02 -0800 Subject: [PATCH 01/19] Add call to initGalleryAutoRefresh --- javascript/gallery.js | 1 + 1 file changed, 1 insertion(+) diff --git a/javascript/gallery.js b/javascript/gallery.js index 51ce478f9..5361cd8c7 100644 --- a/javascript/gallery.js +++ b/javascript/gallery.js @@ -1353,6 +1353,7 @@ async function initGallery() { // triggered on gradio change to monitor when ui monitorGalleries(); updateFolders(); + initGalleryAutoRefresh(); [ 'browser_folders', 'outdir_samples', From 3ba1cfb5406f7c4dd1eb8cbd06df95b10c54fad4 Mon Sep 17 00:00:00 2001 From: awsr <43862868+awsr@users.noreply.github.com> Date: Sat, 7 Feb 2026 14:25:02 -0800 Subject: [PATCH 02/19] Fix typo --- .ruff.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ruff.toml b/.ruff.toml index 9b0bf30d4..443556400 100644 --- a/.ruff.toml +++ b/.ruff.toml @@ -64,7 +64,7 @@ select = [ "ISC", "ICN", "RSE", - "TCH", + "TC", "TID", "INT", "PLE", From 935a4fcb036ec4bfd41f8bd7075e16b3bf8269e4 Mon Sep 17 00:00:00 2001 From: CalamitousFelicitousness Date: Thu, 5 Feb 2026 15:08:46 +0000 Subject: [PATCH 03/19] feat: add Nunchaku group to reference Replace manual Model/TE checkboxes in Quantization Settings with a dedicated "Nunchaku" tab in the Extra Networks menu where users can directly select nunchaku-quantized model variants. Detection is now using a +nunchaku path marker for disambiguation. --- data/reference-nunchaku.json | 183 +++++++++++++++++++++++ javascript/extraNetworks.js | 6 + modules/model_quant.py | 24 ++- modules/shared.py | 2 +- modules/ui_extra_networks.py | 5 +- modules/ui_extra_networks_checkpoints.py | 2 + 6 files changed, 214 insertions(+), 8 deletions(-) create mode 100644 data/reference-nunchaku.json diff --git a/data/reference-nunchaku.json b/data/reference-nunchaku.json new file mode 100644 index 000000000..c58db7385 --- /dev/null +++ b/data/reference-nunchaku.json @@ -0,0 +1,183 @@ +{ + "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, + "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, + "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 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-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 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-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" + } +} diff --git a/javascript/extraNetworks.js b/javascript/extraNetworks.js index acb305da8..eaf516ff2 100644 --- a/javascript/extraNetworks.js +++ b/javascript/extraNetworks.js @@ -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 diff --git a/modules/model_quant.py b/modules/model_quant.py index 1a501be0a..3cad91181 100644 --- a/modules/model_quant.py +++ b/modules/model_quant.py @@ -255,13 +255,25 @@ def check_quant(module: str = ''): def check_nunchaku(module: str = ''): from modules import shared - if module not in shared.opts.nunchaku_quantization: + model_name = getattr(shared.opts, 'sd_model_checkpoint', '') + if '+nunchaku' not in model_name: return False - from modules import mit_nunchaku - mit_nunchaku.install_nunchaku() - if not mit_nunchaku.ok: - return False - return True + base_path = model_name.split('+')[0] + for v in shared.reference_models.values(): + if v.get('path', '') != base_path: + continue + nunchaku_modules = v.get('nunchaku', None) + if nunchaku_modules is None: + continue + if isinstance(nunchaku_modules, bool) and nunchaku_modules: + nunchaku_modules = ['Model', 'TE'] + if not isinstance(nunchaku_modules, list): + continue + if module in nunchaku_modules: + from modules import mit_nunchaku + mit_nunchaku.install_nunchaku() + return mit_nunchaku.ok + return False def create_config(kwargs = None, allow: bool = True, module: str = 'Model', modules_to_not_convert: list = None, modules_dtype_dict: dict = None): diff --git a/modules/shared.py b/modules/shared.py index 91188a825..0d606ab2a 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -281,7 +281,6 @@ options_templates.update(options_section(("quantization", "Model Quantization"), "sdnq_quantize_shuffle_weights": OptionInfo(False, "Shuffle weights in post mode", gr.Checkbox), "nunchaku_sep": OptionInfo("

Nunchaku Engine

", "", gr.HTML), - "nunchaku_quantization": OptionInfo([], "SVDQuant enabled", gr.CheckboxGroup, {"choices": ["Model", "TE"]}), "nunchaku_attention": OptionInfo(False, "Nunchaku attention", gr.Checkbox), "nunchaku_offload": OptionInfo(False, "Nunchaku offloading", gr.Checkbox), @@ -881,6 +880,7 @@ profiler = None import modules.styles prompt_styles = modules.styles.StyleDatabase(opts) reference_models = readfile(os.path.join('data', 'reference.json'), as_type="dict") if opts.extra_network_reference_enable else {} +reference_models.update(readfile(os.path.join('data', 'reference-nunchaku.json'), as_type="dict") if opts.extra_network_reference_enable else {}) cmd_opts.disable_extension_access = (cmd_opts.share or cmd_opts.listen or (cmd_opts.server_name or False)) and not cmd_opts.insecure log.debug('Initializing: devices') diff --git a/modules/ui_extra_networks.py b/modules/ui_extra_networks.py index 3de17896f..d98e7a14b 100644 --- a/modules/ui_extra_networks.py +++ b/modules/ui_extra_networks.py @@ -305,6 +305,7 @@ class ExtraNetworksPage: subdirs['Reference'] = 1 subdirs['Distilled'] = 1 subdirs['Quantized'] = 1 + subdirs['Nunchaku'] = 1 subdirs['Community'] = 1 subdirs['Cloud'] = 1 subdirs[diffusers_base] = 1 @@ -324,6 +325,8 @@ class ExtraNetworksPage: subdirs.move_to_end('Distilled', last=True) if 'Quantized' in subdirs: subdirs.move_to_end('Quantized', last=True) + if 'Nunchaku' in subdirs: + subdirs.move_to_end('Nunchaku', last=True) if 'Community' in subdirs: subdirs.move_to_end('Community', last=True) if 'Cloud' in subdirs: @@ -332,7 +335,7 @@ class ExtraNetworksPage: for subdir in subdirs: if len(subdir) == 0: continue - if subdir in ['All', 'Local', 'Diffusers', 'Reference', 'Distilled', 'Quantized', 'Community', 'Cloud']: + if subdir in ['All', 'Local', 'Diffusers', 'Reference', 'Distilled', 'Quantized', 'Nunchaku', 'Community', 'Cloud']: style = 'network-reference' else: style = 'network-folder' diff --git a/modules/ui_extra_networks_checkpoints.py b/modules/ui_extra_networks_checkpoints.py index 9470ec9bb..402037a23 100644 --- a/modules/ui_extra_networks_checkpoints.py +++ b/modules/ui_extra_networks_checkpoints.py @@ -48,12 +48,14 @@ class ExtraNetworksPageCheckpoints(ui_extra_networks.ExtraNetworksPage): reference_distilled = readfile(os.path.join('data', 'reference-distilled.json'), as_type="dict") reference_community = readfile(os.path.join('data', 'reference-community.json'), as_type="dict") reference_cloud = readfile(os.path.join('data', 'reference-cloud.json'), as_type="dict") + reference_nunchaku = readfile(os.path.join('data', 'reference-nunchaku.json'), as_type="dict") shared.reference_models = {} shared.reference_models.update(reference_base) shared.reference_models.update(reference_quant) shared.reference_models.update(reference_community) shared.reference_models.update(reference_distilled) shared.reference_models.update(reference_cloud) + shared.reference_models.update(reference_nunchaku) for k, v in shared.reference_models.items(): count['total'] += 1 From 8ff7074da555b820b58ed42afd98690fcbd737eb Mon Sep 17 00:00:00 2001 From: CalamitousFelicitousness Date: Thu, 5 Feb 2026 21:31:49 +0000 Subject: [PATCH 04/19] feat: add Nunchaku variants for Fill and Depth in Flux Tools - Add 'Fill (Nunchaku)' and 'Depth (Nunchaku)' options to Flux Tools dropdown, loading models with +nunchaku suffix for SVDQuant quantization - Mark Fill and Depth nunchaku reference entries as hidden so they remain available for check_nunchaku() lookup but don't appear in Extra Networks - Filter hidden reference models in ui_extra_networks_checkpoints --- data/reference-nunchaku.json | 2 ++ modules/ui_extra_networks_checkpoints.py | 3 +++ scripts/flux_tools.py | 22 +++++++++++++--------- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/data/reference-nunchaku.json b/data/reference-nunchaku.json index c58db7385..75d0d4a92 100644 --- a/data/reference-nunchaku.json +++ b/data/reference-nunchaku.json @@ -50,6 +50,7 @@ "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, @@ -61,6 +62,7 @@ "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, diff --git a/modules/ui_extra_networks_checkpoints.py b/modules/ui_extra_networks_checkpoints.py index 402037a23..160fa3327 100644 --- a/modules/ui_extra_networks_checkpoints.py +++ b/modules/ui_extra_networks_checkpoints.py @@ -60,6 +60,9 @@ class ExtraNetworksPageCheckpoints(ui_extra_networks.ExtraNetworksPage): for k, v in shared.reference_models.items(): count['total'] += 1 url = v['path'] + if v.get('hidden', False): + count['hidden'] += 1 + continue experimental = v.get('experimental', False) if experimental: if shared.cmd_opts.experimental: diff --git a/scripts/flux_tools.py b/scripts/flux_tools.py index aa0358ce1..b2e87c36c 100644 --- a/scripts/flux_tools.py +++ b/scripts/flux_tools.py @@ -25,7 +25,7 @@ class Script(scripts_manager.Script): with gr.Row(): gr.HTML('  Flux.1 Redux
') with gr.Row(): - tool = gr.Dropdown(label='Tool', choices=['None', 'Redux', 'Fill', 'Canny', 'Depth'], value='None') + tool = gr.Dropdown(label='Tool', choices=['None', 'Redux', 'Fill', 'Fill (Nunchaku)', 'Canny', 'Depth', 'Depth (Nunchaku)'], value='None') with gr.Row(): prompt = gr.Slider(label='Redux prompt strength', minimum=0, maximum=2, step=0.01, value=0, visible=False) process = gr.Checkbox(label='Control preprocess input images', value=True, visible=False) @@ -34,8 +34,8 @@ class Script(scripts_manager.Script): def display(tool): return [ gr.update(visible=tool in ['Redux']), - gr.update(visible=tool in ['Canny', 'Depth']), - gr.update(visible=tool in ['Canny', 'Depth']), + gr.update(visible=tool in ['Canny', 'Depth', 'Depth (Nunchaku)']), + gr.update(visible=tool in ['Canny', 'Depth', 'Depth (Nunchaku)']), ] tool.change(fn=display, inputs=[tool], outputs=[prompt, process, strength]) @@ -91,13 +91,15 @@ class Script(scripts_manager.Script): shared.log.debug(f'{title}: tool=Redux unload') redux_pipe = None - if tool == 'Fill': + if tool in ['Fill', 'Fill (Nunchaku)']: # pipe = FluxFillPipeline.from_pretrained("black-forest-labs/FLUX.1-Fill-dev", torch_dtype=torch.bfloat16, revision="refs/pr/4").to("cuda") if p.image_mask is None: shared.log.error(f'{title}: tool={tool} no image_mask') return None - if shared.sd_model.__class__.__name__ != 'FluxFillPipeline': - shared.opts.data["sd_model_checkpoint"] = "black-forest-labs/FLUX.1-Fill-dev" + nunchaku_suffix = '+nunchaku' if tool == 'Fill (Nunchaku)' else '' + checkpoint = f"black-forest-labs/FLUX.1-Fill-dev{nunchaku_suffix}" + if shared.sd_model.__class__.__name__ != 'FluxFillPipeline' or shared.opts.sd_model_checkpoint != checkpoint: + shared.opts.data["sd_model_checkpoint"] = checkpoint sd_models.reload_model_weights(op='model', revision="refs/pr/4") p.task_args['image'] = image p.task_args['mask_image'] = p.image_mask @@ -124,11 +126,13 @@ class Script(scripts_manager.Script): shared.log.debug(f'{title}: tool=Canny unload processor') processor_canny = None - if tool == 'Depth': + if tool in ['Depth', 'Depth (Nunchaku)']: # pipe = diffusers.FluxControlPipeline.from_pretrained("black-forest-labs/FLUX.1-Depth-dev", torch_dtype=torch.bfloat16, revision="refs/pr/1").to("cuda") install('git+https://github.com/huggingface/image_gen_aux.git', 'image_gen_aux') - if shared.sd_model.__class__.__name__ != 'FluxControlPipeline' or 'Depth' not in shared.opts.sd_model_checkpoint: - shared.opts.data["sd_model_checkpoint"] = "black-forest-labs/FLUX.1-Depth-dev" + nunchaku_suffix = '+nunchaku' if tool == 'Depth (Nunchaku)' else '' + checkpoint = f"black-forest-labs/FLUX.1-Depth-dev{nunchaku_suffix}" + if shared.sd_model.__class__.__name__ != 'FluxControlPipeline' or shared.opts.sd_model_checkpoint != checkpoint: + shared.opts.data["sd_model_checkpoint"] = checkpoint sd_models.reload_model_weights(op='model', revision="refs/pr/1") if processor_depth is None: from image_gen_aux import DepthPreprocessor From a2ee885e28b6a739c090b40392df78327492759b Mon Sep 17 00:00:00 2001 From: CalamitousFelicitousness Date: Thu, 5 Feb 2026 21:35:51 +0000 Subject: [PATCH 05/19] refactor: update nunchaku repo URLs and version handling - Rename HuggingFace org from nunchaku-tech to nunchaku-ai across all nunchaku model repos (flux, sdxl, sana, z-image, qwen, t5) - Add per-torch-version nunchaku version mapping instead of single global version, with robust torch version parsing --- modules/mit_nunchaku.py | 30 +++++++++++++++++++++++++----- modules/sd_unet.py | 4 ++-- pipelines/flux/flux_nunchaku.py | 14 +++++++------- pipelines/generic.py | 2 +- pipelines/model_sana.py | 2 +- pipelines/model_z_image.py | 2 +- pipelines/qwen/qwen_nunchaku.py | 10 +++++----- 7 files changed, 42 insertions(+), 22 deletions(-) diff --git a/modules/mit_nunchaku.py b/modules/mit_nunchaku.py index b5e82c1da..6b4e524cc 100644 --- a/modules/mit_nunchaku.py +++ b/modules/mit_nunchaku.py @@ -4,10 +4,27 @@ from installer import log, pip from modules import devices -nunchaku_ver = '1.1.0' +nunchaku_versions = { + '2.5': '1.0.1', + '2.6': '1.0.1', + '2.7': '1.1.0', + '2.8': '1.1.0', + '2.9': '1.1.0', + '2.10': '1.0.2', + '2.11': '1.1.0', +} ok = False +def _expected_ver(): + try: + import torch + torch_ver = '.'.join(torch.__version__.split('+')[0].split('.')[:2]) + return nunchaku_versions.get(torch_ver) + except Exception: + return None + + def check(): global ok # pylint: disable=global-statement if ok: @@ -16,8 +33,9 @@ def check(): import nunchaku import nunchaku.utils from nunchaku import __version__ + expected = _expected_ver() log.info(f'Nunchaku: path={nunchaku.__path__} version={__version__.__version__} precision={nunchaku.utils.get_precision()}') - if __version__.__version__ != nunchaku_ver: + if expected is not None and __version__.__version__ != expected: ok = False return False ok = True @@ -49,14 +67,16 @@ def install_nunchaku(): if devices.backend not in ['cuda']: log.error(f'Nunchaku: backend={devices.backend} unsupported') return False - torch_ver = torch.__version__[:3] - if torch_ver not in ['2.5', '2.6', '2.7', '2.8', '2.9', '2.10']: + torch_ver = '.'.join(torch.__version__.split('+')[0].split('.')[:2]) + nunchaku_ver = nunchaku_versions.get(torch_ver) + if nunchaku_ver is None: log.error(f'Nunchaku: torch={torch.__version__} unsupported') + return False suffix = 'x86_64' if arch == 'linux' else 'win_amd64' url = os.environ.get('NUNCHAKU_COMMAND', None) if url is None: arch = f'{arch}_' if arch == 'linux' else '' - url = f'https://huggingface.co/nunchaku-tech/nunchaku/resolve/main/nunchaku-{nunchaku_ver}' + url = f'https://huggingface.co/nunchaku-ai/nunchaku/resolve/main/nunchaku-{nunchaku_ver}' url += f'+torch{torch_ver}-cp{python_ver}-cp{python_ver}-{arch}{suffix}.whl' cmd = f'install --upgrade {url}' log.debug(f'Nunchaku: install="{url}"') diff --git a/modules/sd_unet.py b/modules/sd_unet.py index c73ca8dc5..55a8e9b7e 100644 --- a/modules/sd_unet.py +++ b/modules/sd_unet.py @@ -18,9 +18,9 @@ def load_unet_sdxl_nunchaku(repo_id): shared.log.error(f'Load module: quant=Nunchaku module=unet repo="{repo_id}" low nunchaku version') return None if 'turbo' in repo_id.lower(): - nunchaku_repo = 'nunchaku-tech/nunchaku-sdxl-turbo/svdq-int4_r32-sdxl-turbo.safetensors' + nunchaku_repo = 'nunchaku-ai/nunchaku-sdxl-turbo/svdq-int4_r32-sdxl-turbo.safetensors' else: - nunchaku_repo = 'nunchaku-tech/nunchaku-sdxl/svdq-int4_r32-sdxl.safetensors' + nunchaku_repo = 'nunchaku-ai/nunchaku-sdxl/svdq-int4_r32-sdxl.safetensors' shared.log.debug(f'Load module: quant=Nunchaku module=unet repo="{nunchaku_repo}" offload={shared.opts.nunchaku_offload}') unet = NunchakuSDXLUNet2DConditionModel.from_pretrained( diff --git a/pipelines/flux/flux_nunchaku.py b/pipelines/flux/flux_nunchaku.py index 9a737d103..d8761e186 100644 --- a/pipelines/flux/flux_nunchaku.py +++ b/pipelines/flux/flux_nunchaku.py @@ -9,19 +9,19 @@ def load_flux_nunchaku(repo_id): if 'srpo' in repo_id.lower(): pass elif 'flux.1-dev' in repo_id.lower(): - nunchaku_repo = f"nunchaku-tech/nunchaku-flux.1-dev/svdq-{nunchaku_precision}_r32-flux.1-dev.safetensors" + nunchaku_repo = f"nunchaku-ai/nunchaku-flux.1-dev/svdq-{nunchaku_precision}_r32-flux.1-dev.safetensors" elif 'flux.1-schnell' in repo_id.lower(): - nunchaku_repo = f"nunchaku-tech/nunchaku-flux.1-schnell/svdq-{nunchaku_precision}_r32-flux.1-schnell.safetensors" + nunchaku_repo = f"nunchaku-ai/nunchaku-flux.1-schnell/svdq-{nunchaku_precision}_r32-flux.1-schnell.safetensors" elif 'flux.1-kontext' in repo_id.lower(): - nunchaku_repo = f"nunchaku-tech/nunchaku-flux.1-kontext-dev/svdq-{nunchaku_precision}_r32-flux.1-kontext-dev.safetensors" + nunchaku_repo = f"nunchaku-ai/nunchaku-flux.1-kontext-dev/svdq-{nunchaku_precision}_r32-flux.1-kontext-dev.safetensors" elif 'flux.1-krea' in repo_id.lower(): - nunchaku_repo = f"nunchaku-tech/nunchaku-flux.1-krea-dev/svdq-{nunchaku_precision}_r32-flux.1-krea-dev.safetensors" + nunchaku_repo = f"nunchaku-ai/nunchaku-flux.1-krea-dev/svdq-{nunchaku_precision}_r32-flux.1-krea-dev.safetensors" elif 'flux.1-fill' in repo_id.lower(): - nunchaku_repo = f"nunchaku-tech/nunchaku-flux.1-fill-dev/svdq-{nunchaku_precision}-flux.1-fill-dev.safetensors" + nunchaku_repo = f"nunchaku-ai/nunchaku-flux.1-fill-dev/svdq-{nunchaku_precision}-flux.1-fill-dev.safetensors" elif 'flux.1-depth' in repo_id.lower(): - nunchaku_repo = f"nunchaku-tech/nunchaku-flux.1-depth-dev/svdq-{nunchaku_precision}-flux.1-depth-dev.safetensors" + nunchaku_repo = f"nunchaku-ai/nunchaku-flux.1-depth-dev/svdq-{nunchaku_precision}-flux.1-depth-dev.safetensors" elif 'shuttle' in repo_id.lower(): - nunchaku_repo = f"nunchaku-tech/nunchaku-shuttle-jaguar/svdq-{nunchaku_precision}-shuttle-jaguar.safetensors" + nunchaku_repo = f"nunchaku-ai/nunchaku-shuttle-jaguar/svdq-{nunchaku_precision}-shuttle-jaguar.safetensors" else: shared.log.error(f'Load module: quant=Nunchaku module=transformer repo="{repo_id}" unsupported') if nunchaku_repo is not None: diff --git a/pipelines/generic.py b/pipelines/generic.py index 6ad9a3fd5..3b1b4bc66 100644 --- a/pipelines/generic.py +++ b/pipelines/generic.py @@ -152,7 +152,7 @@ def load_text_encoder(repo_id, cls_name, load_config=None, subfolder="text_encod elif cls_name == transformers.T5EncoderModel and allow_shared and shared.opts.te_shared_t5: if model_quant.check_nunchaku('TE'): import nunchaku - repo_id = 'nunchaku-tech/nunchaku-t5/awq-int4-flux.1-t5xxl.safetensors' + repo_id = 'nunchaku-ai/nunchaku-t5/awq-int4-flux.1-t5xxl.safetensors' cls_name = nunchaku.NunchakuT5EncoderModel shared.log.debug(f'Load model: text_encoder="{repo_id}" cls={cls_name.__name__} quant="SVDQuant" loader={_loader("transformers")}') text_encoder = nunchaku.NunchakuT5EncoderModel.from_pretrained( diff --git a/pipelines/model_sana.py b/pipelines/model_sana.py index 73da28472..8b04fcfb0 100644 --- a/pipelines/model_sana.py +++ b/pipelines/model_sana.py @@ -9,7 +9,7 @@ def load_quants(kwargs, repo_id, cache_dir): if 'Sana_1600M_1024px' in repo_id and model_quant.check_nunchaku('Model'): # only available model import nunchaku nunchaku_precision = nunchaku.utils.get_precision() - nunchaku_repo = "nunchaku-tech/nunchaku-sana/svdq-int4_r32-sana1.6b.safetensors" + nunchaku_repo = "nunchaku-ai/nunchaku-sana/svdq-int4_r32-sana1.6b.safetensors" shared.log.debug(f'Load module: quant=Nunchaku module=transformer repo="{nunchaku_repo}" precision={nunchaku_precision} attention={shared.opts.nunchaku_attention}') kwargs['transformer'] = nunchaku.NunchakuSanaTransformer2DModel.from_pretrained(nunchaku_repo, torch_dtype=devices.dtype, cache_dir=cache_dir) elif model_quant.check_quant('Model'): diff --git a/pipelines/model_z_image.py b/pipelines/model_z_image.py index 9f8dd51e1..3aa269b48 100644 --- a/pipelines/model_z_image.py +++ b/pipelines/model_z_image.py @@ -8,7 +8,7 @@ def load_nunchaku(): import nunchaku nunchaku_precision = nunchaku.utils.get_precision() nunchaku_rank = 128 - nunchaku_repo = f"nunchaku-tech/nunchaku-z-image-turbo/svdq-{nunchaku_precision}_r{nunchaku_rank}-z-image-turbo.safetensors" + nunchaku_repo = f"nunchaku-ai/nunchaku-z-image-turbo/svdq-{nunchaku_precision}_r{nunchaku_rank}-z-image-turbo.safetensors" shared.log.debug(f'Load module: quant=Nunchaku module=transformer repo="{nunchaku_repo}" attention={shared.opts.nunchaku_attention}') transformer = nunchaku.NunchakuZImageTransformer2DModel.from_pretrained( nunchaku_repo, diff --git a/pipelines/qwen/qwen_nunchaku.py b/pipelines/qwen/qwen_nunchaku.py index 4c89b7b1c..b9f331f60 100644 --- a/pipelines/qwen/qwen_nunchaku.py +++ b/pipelines/qwen/qwen_nunchaku.py @@ -14,15 +14,15 @@ def load_qwen_nunchaku(repo_id): if 'pruning' in repo_id.lower() or 'distill' in repo_id.lower(): return None elif repo_id.lower().endswith('qwen-image'): - nunchaku_repo = f"nunchaku-tech/nunchaku-qwen-image/svdq-{nunchaku_precision}_r128-qwen-image.safetensors" # r32 vs r128 + nunchaku_repo = f"nunchaku-ai/nunchaku-qwen-image/svdq-{nunchaku_precision}_r128-qwen-image.safetensors" # r32 vs r128 elif repo_id.lower().endswith('qwen-lightning'): - nunchaku_repo = f"nunchaku-tech/nunchaku-qwen-image/svdq-{nunchaku_precision}_r128-qwen-image-lightningv1.1-8steps.safetensors" # 8-step variant + nunchaku_repo = f"nunchaku-ai/nunchaku-qwen-image/svdq-{nunchaku_precision}_r128-qwen-image-lightningv1.1-8steps.safetensors" # 8-step variant elif repo_id.lower().endswith('qwen-image-edit-2509'): - nunchaku_repo = f"nunchaku-tech/nunchaku-qwen-image-edit-2509/svdq-{nunchaku_precision}_r128-qwen-image-edit-2509.safetensors" # 8-step variant + nunchaku_repo = f"nunchaku-ai/nunchaku-qwen-image-edit-2509/svdq-{nunchaku_precision}_r128-qwen-image-edit-2509.safetensors" # 8-step variant elif repo_id.lower().endswith('qwen-image-edit'): - nunchaku_repo = f"nunchaku-tech/nunchaku-qwen-image-edit/svdq-{nunchaku_precision}_r128-qwen-image-edit.safetensors" # 8-step variant + nunchaku_repo = f"nunchaku-ai/nunchaku-qwen-image-edit/svdq-{nunchaku_precision}_r128-qwen-image-edit.safetensors" # 8-step variant elif repo_id.lower().endswith('qwen-lightning-edit'): - nunchaku_repo = f"nunchaku-tech/nunchaku-qwen-image-edit/svdq-{nunchaku_precision}_r128-qwen-image-edit-lightningv1.0-8steps.safetensors" # 8-step variant + nunchaku_repo = f"nunchaku-ai/nunchaku-qwen-image-edit/svdq-{nunchaku_precision}_r128-qwen-image-edit-lightningv1.0-8steps.safetensors" # 8-step variant else: shared.log.error(f'Load module: quant=Nunchaku module=transformer repo="{repo_id}" unsupported') if nunchaku_repo is not None: From c8597ca84e6aa72be7919d2c51d3975c52c9ca1a Mon Sep 17 00:00:00 2001 From: CalamitousFelicitousness Date: Fri, 6 Feb 2026 00:10:18 +0000 Subject: [PATCH 06/19] feat: hide Nunchaku reference models on non-CUDA backends Filter out reference entries tagged "nunchaku" from Extra Networks when the active backend is not CUDA, since Nunchaku requires NVIDIA GPUs. Entries remain in shared.reference_models for programmatic lookup but are not yielded to the UI. --- modules/ui_extra_networks_checkpoints.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/ui_extra_networks_checkpoints.py b/modules/ui_extra_networks_checkpoints.py index 160fa3327..b179d55c6 100644 --- a/modules/ui_extra_networks_checkpoints.py +++ b/modules/ui_extra_networks_checkpoints.py @@ -3,7 +3,7 @@ import html import json import concurrent from datetime import datetime -from modules import shared, ui_extra_networks, sd_models, modelstats, paths +from modules import shared, ui_extra_networks, sd_models, modelstats, paths, devices from modules.json_helpers import readfile @@ -88,6 +88,9 @@ class ExtraNetworksPageCheckpoints(ui_extra_networks.ExtraNetworksPage): path = f'{v.get("path", "")}' tag = v.get('tags', '') + if tag == 'nunchaku' and devices.backend != 'cuda': + count['hidden'] += 1 + continue if tag in count: count[tag] += 1 elif tag != '': From 33de04a0c76ade5261be43f22af1358a6cfa9120 Mon Sep 17 00:00:00 2001 From: CalamitousFelicitousness Date: Fri, 6 Feb 2026 00:10:27 +0000 Subject: [PATCH 07/19] feat: add 4-step Nunchaku variants for Qwen-Lightning models Add 4-step distilled Nunchaku SVDQuant entries for Qwen-Lightning and Qwen-Lightning-Edit alongside the existing 8-step variants. Step count is now shown in the reference name (e.g. "Qwen-Lightning (4-step)"). - Add subfolder parameter to load_qwen_nunchaku to distinguish 4-step (nunchaku-4step) from 8-step (nunchaku) variants - Route to correct safetensors: lightningv1.0-4steps vs lightningv1.1-8steps for gen, lightningv1.0-4steps vs lightningv1.0-8steps for edit - Strip nunchaku subfolder before pipeline from_pretrained since it does not exist in the base HuggingFace repos --- data/reference-nunchaku.json | 28 ++++++++++++++++++++++++++-- pipelines/model_qwen.py | 4 +++- pipelines/qwen/qwen_nunchaku.py | 19 +++++++++++++------ 3 files changed, 42 insertions(+), 9 deletions(-) diff --git a/data/reference-nunchaku.json b/data/reference-nunchaku.json index 75d0d4a92..a22be9e91 100644 --- a/data/reference-nunchaku.json +++ b/data/reference-nunchaku.json @@ -90,7 +90,7 @@ "size": 0, "date": "2025 June" }, - "Qwen-Lightning Nunchaku SVDQuant": { + "Qwen-Lightning (8-step) Nunchaku SVDQuant": { "path": "vladmandic/Qwen-Lightning", "subfolder": "nunchaku", "preview": "vladmandic--Qwen-Lightning.jpg", @@ -102,6 +102,18 @@ "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", @@ -113,7 +125,7 @@ "size": 0, "date": "2025 June" }, - "Qwen-Lightning-Edit Nunchaku SVDQuant": { + "Qwen-Lightning-Edit (8-step) Nunchaku SVDQuant": { "path": "vladmandic/Qwen-Lightning-Edit", "subfolder": "nunchaku", "preview": "vladmandic--Qwen-Lightning-Edit.jpg", @@ -125,6 +137,18 @@ "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", diff --git a/pipelines/model_qwen.py b/pipelines/model_qwen.py index 3bea5c121..546e755cc 100644 --- a/pipelines/model_qwen.py +++ b/pipelines/model_qwen.py @@ -37,7 +37,7 @@ def load_qwen(checkpoint_info, diffusers_load_config=None): diffusers.pipelines.auto_pipeline.AUTO_INPAINT_PIPELINES_MAPPING["qwen-image"] = diffusers.QwenImageInpaintPipeline if model_quant.check_nunchaku('Model'): - transformer = qwen.load_qwen_nunchaku(repo_id) + transformer = qwen.load_qwen_nunchaku(repo_id, subfolder=repo_subfolder) if 'Qwen-Image-Distill-Full' in repo_id: repo_transformer = repo_id @@ -63,6 +63,8 @@ def load_qwen(checkpoint_info, diffusers_load_config=None): text_encoder = generic.load_text_encoder(repo_te, cls_name=transformers.Qwen2_5_VLForConditionalGeneration, load_config=diffusers_load_config) repo_id, repo_subfolder = qwen.check_qwen_pruning(repo_id, repo_subfolder) + if repo_subfolder is not None and repo_subfolder.startswith('nunchaku'): + repo_subfolder = None pipe = cls_name.from_pretrained( repo_id, transformer=transformer, diff --git a/pipelines/qwen/qwen_nunchaku.py b/pipelines/qwen/qwen_nunchaku.py index b9f331f60..4fd964df3 100644 --- a/pipelines/qwen/qwen_nunchaku.py +++ b/pipelines/qwen/qwen_nunchaku.py @@ -1,11 +1,12 @@ from modules import shared, devices -def load_qwen_nunchaku(repo_id): +def load_qwen_nunchaku(repo_id, subfolder=None): import nunchaku nunchaku_precision = nunchaku.utils.get_precision() nunchaku_repo = None transformer = None + four_step = subfolder is not None and '4step' in subfolder try: from nunchaku.models.transformers.transformer_qwenimage import NunchakuQwenImageTransformer2DModel except Exception: @@ -14,15 +15,21 @@ def load_qwen_nunchaku(repo_id): if 'pruning' in repo_id.lower() or 'distill' in repo_id.lower(): return None elif repo_id.lower().endswith('qwen-image'): - nunchaku_repo = f"nunchaku-ai/nunchaku-qwen-image/svdq-{nunchaku_precision}_r128-qwen-image.safetensors" # r32 vs r128 + nunchaku_repo = f"nunchaku-ai/nunchaku-qwen-image/svdq-{nunchaku_precision}_r128-qwen-image.safetensors" elif repo_id.lower().endswith('qwen-lightning'): - nunchaku_repo = f"nunchaku-ai/nunchaku-qwen-image/svdq-{nunchaku_precision}_r128-qwen-image-lightningv1.1-8steps.safetensors" # 8-step variant + if four_step: + nunchaku_repo = f"nunchaku-ai/nunchaku-qwen-image/svdq-{nunchaku_precision}_r128-qwen-image-lightningv1.0-4steps.safetensors" + else: + nunchaku_repo = f"nunchaku-ai/nunchaku-qwen-image/svdq-{nunchaku_precision}_r128-qwen-image-lightningv1.1-8steps.safetensors" elif repo_id.lower().endswith('qwen-image-edit-2509'): - nunchaku_repo = f"nunchaku-ai/nunchaku-qwen-image-edit-2509/svdq-{nunchaku_precision}_r128-qwen-image-edit-2509.safetensors" # 8-step variant + nunchaku_repo = f"nunchaku-ai/nunchaku-qwen-image-edit-2509/svdq-{nunchaku_precision}_r128-qwen-image-edit-2509.safetensors" elif repo_id.lower().endswith('qwen-image-edit'): - nunchaku_repo = f"nunchaku-ai/nunchaku-qwen-image-edit/svdq-{nunchaku_precision}_r128-qwen-image-edit.safetensors" # 8-step variant + nunchaku_repo = f"nunchaku-ai/nunchaku-qwen-image-edit/svdq-{nunchaku_precision}_r128-qwen-image-edit.safetensors" elif repo_id.lower().endswith('qwen-lightning-edit'): - nunchaku_repo = f"nunchaku-ai/nunchaku-qwen-image-edit/svdq-{nunchaku_precision}_r128-qwen-image-edit-lightningv1.0-8steps.safetensors" # 8-step variant + if four_step: + nunchaku_repo = f"nunchaku-ai/nunchaku-qwen-image-edit/svdq-{nunchaku_precision}_r128-qwen-image-edit-lightningv1.0-4steps.safetensors" + else: + nunchaku_repo = f"nunchaku-ai/nunchaku-qwen-image-edit/svdq-{nunchaku_precision}_r128-qwen-image-edit-lightningv1.0-8steps.safetensors" else: shared.log.error(f'Load module: quant=Nunchaku module=transformer repo="{repo_id}" unsupported') if nunchaku_repo is not None: From 5000401647e354872a0bc05b11060ddf55d6c179 Mon Sep 17 00:00:00 2001 From: CalamitousFelicitousness Date: Fri, 6 Feb 2026 00:13:06 +0000 Subject: [PATCH 08/19] docs(ui): add tooltips for Nunchaku attention and offload settings --- html/locale_en.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/html/locale_en.json b/html/locale_en.json index 5eb0b9f89..599bf7de5 100644 --- a/html/locale_en.json +++ b/html/locale_en.json @@ -337,6 +337,8 @@ {"id":"","label":"Model Options","localized":"","reload":"","hint":"Settings related to behavior of specific models"}, {"id":"","label":"Model Offloading","localized":"","reload":"","hint":"Settings related to model offloading and memory management"}, {"id":"","label":"Model Quantization","localized":"","reload":"","hint":"Settings related to model quantization which is used to reduce memory usage"}, + {"id":"","label":"Nunchaku attention","localized":"","reload":"","hint":"Replaces default attention with Nunchaku's custom FP16 attention kernel for faster inference on consumer NVIDIA GPUs.
Might provide performance improvement on GPUs which have higher FP16 tensor cores throughput than BF16.

Currently only affects Flux-based models (Dev, Schnell, Kontext, Fill, Depth, etc.). Has no effect on Qwen, SDXL, Sana, or other architectures.

Disabled by default."}, + {"id":"","label":"Nunchaku offloading","localized":"","reload":"","hint":"Enables Nunchaku's own per-block CPU offloading with asynchronous CUDA streams to reduce VRAM usage.
Uses a ping-pong buffer strategy: while one transformer block computes on GPU, the next block preloads from CPU in the background, hiding most of the transfer latency.

Can reduce VRAM usage at the cost of slower inference.
This replaces SD.Next's pipeline offloading for the transformer component.

Only useful on low-VRAM GPUs. If your GPU has enough memory to hold the quantized model (16+ GB), keep this disabled for maximum speed.
Supports Flux and Qwen models. Not supported for SDXL where this setting is ignored.
Disabled by default."}, {"id":"","label":"Image Metadata","localized":"","reload":"","hint":"Settings related to handling of metadata that is created with generated images"}, {"id":"","label":"Legacy Options","localized":"","reload":"","hint":"Settings related to legacy options - should not be used"}, {"id":"","label":"Restart server","localized":"","reload":"","hint":"Restart server"}, From 34a18c90988d1675935d81ef3d8826b77811c8cf Mon Sep 17 00:00:00 2001 From: CalamitousFelicitousness Date: Sat, 7 Feb 2026 22:24:15 +0000 Subject: [PATCH 09/19] fix: skip Nunchaku offloading for SDXL models Nunchaku's SDXL UNet does not support offloading and raises NotImplementedError when offload=True is passed. Skip the parameter for SDXL and log a warning instead of crashing. --- modules/sd_unet.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/sd_unet.py b/modules/sd_unet.py index 55a8e9b7e..643d1c08e 100644 --- a/modules/sd_unet.py +++ b/modules/sd_unet.py @@ -22,10 +22,11 @@ def load_unet_sdxl_nunchaku(repo_id): else: nunchaku_repo = 'nunchaku-ai/nunchaku-sdxl/svdq-int4_r32-sdxl.safetensors' - shared.log.debug(f'Load module: quant=Nunchaku module=unet repo="{nunchaku_repo}" offload={shared.opts.nunchaku_offload}') + if shared.opts.nunchaku_offload: + shared.log.warning('Load module: quant=Nunchaku module=unet offload not supported for SDXL, ignoring') + shared.log.debug(f'Load module: quant=Nunchaku module=unet repo="{nunchaku_repo}"') unet = NunchakuSDXLUNet2DConditionModel.from_pretrained( nunchaku_repo, - offload=shared.opts.nunchaku_offload, torch_dtype=devices.dtype, cache_dir=shared.opts.hfcache_dir, ) From 4162fd84db575b6d43163ddd9389ba52016db51b Mon Sep 17 00:00:00 2001 From: CalamitousFelicitousness Date: Mon, 9 Feb 2026 01:38:44 +0000 Subject: [PATCH 10/19] fix(installer): handle setuptools 82 removing pkg_resources and uv broken fallback - Build CLIP with --no-build-isolation to use venv's setuptools 69.5.1 instead of pip pulling setuptools 82.0.0 (which removed pkg_resources) into an isolated build environment - Add cleanup_broken_packages() to remove dist-info directories with missing RECORD files before falling back from uv to pip, preventing cascading install failures when uv partially installs packages - Add no_build_isolation parameter to install() function --- installer.py | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/installer.py b/installer.py index 0b8d011a9..28356e63f 100644 --- a/installer.py +++ b/installer.py @@ -431,6 +431,26 @@ def run(cmd: str, arg: str): return txt +def cleanup_broken_packages(): + """Remove dist-info directories with missing RECORD files that uv may have left behind""" + try: + import site + for site_dir in site.getsitepackages(): + if not os.path.isdir(site_dir): + continue + for entry in os.listdir(site_dir): + if not entry.endswith('.dist-info'): + continue + dist_info = os.path.join(site_dir, entry) + record_file = os.path.join(dist_info, 'RECORD') + if not os.path.isfile(record_file): + pkg_name = entry.split('-')[0] + log.warning(f'Install: removing broken package metadata: {pkg_name} path={dist_info}') + shutil.rmtree(dist_info, ignore_errors=True) + except Exception: + pass + + def pip(arg: str, ignore: bool = False, quiet: bool = True, uv = True): t_start = time.time() originalArg = arg @@ -454,6 +474,7 @@ def pip(arg: str, ignore: bool = False, quiet: bool = True, uv = True): err = result.stderr.decode(encoding="utf8", errors="ignore") log.warning(f'Install: cmd="{pipCmd}" args="{all_args}" cannot use uv, fallback to pip') debug(f'Install: uv pip error: {err}') + cleanup_broken_packages() return pip(originalArg, ignore, quiet, uv=False) else: txt += ('\n' if len(txt) > 0 else '') + result.stderr.decode(encoding="utf8", errors="ignore") @@ -468,7 +489,7 @@ def pip(arg: str, ignore: bool = False, quiet: bool = True, uv = True): # install package using pip if not already installed -def install(package, friendly: str = None, ignore: bool = False, reinstall: bool = False, no_deps: bool = False, quiet: bool = False, force: bool = False): +def install(package, friendly: str = None, ignore: bool = False, reinstall: bool = False, no_deps: bool = False, quiet: bool = False, force: bool = False, no_build_isolation: bool = False): t_start = time.time() res = '' if args.reinstall or args.upgrade: @@ -476,7 +497,8 @@ def install(package, friendly: str = None, ignore: bool = False, reinstall: bool quick_allowed = False if (args.reinstall) or (reinstall) or (not installed(package, friendly, quiet=quiet)): deps = '' if not no_deps else '--no-deps ' - cmd = f"install{' --upgrade' if not args.uv else ''}{' --force-reinstall' if force else ''} {deps}{package}" + isolation = '' if not no_build_isolation else '--no-build-isolation ' + cmd = f"install{' --upgrade' if not args.uv else ''}{' --force-reinstall' if force else ''} {deps}{isolation}{package}" res = pip(cmd, ignore=ignore, uv=package != "uv" and not package.startswith('git+')) try: importlib.reload(pkg_resources) @@ -1100,7 +1122,7 @@ def install_packages(): pr.enable() # log.info('Install: verifying packages') clip_package = os.environ.get('CLIP_PACKAGE', "git+https://github.com/openai/CLIP.git") - install(clip_package, 'clip', quiet=True) + install(clip_package, 'clip', quiet=True, no_build_isolation=True) install('open-clip-torch', no_deps=True, quiet=True) # tensorflow_package = os.environ.get('TENSORFLOW_PACKAGE', 'tensorflow==2.13.0') # tensorflow_package = os.environ.get('TENSORFLOW_PACKAGE', None) From 0d2e9fbf62a1fc3d72bf695cc6bbda38a2f58b74 Mon Sep 17 00:00:00 2001 From: vladmandic Date: Mon, 9 Feb 2026 18:20:10 +0100 Subject: [PATCH 11/19] cleanup and update changelog Signed-off-by: vladmandic --- CHANGELOG.md | 7 +- cli/benchmark_attention.py | 117 +++++++++++------------ modules/shared.py | 1 - modules/ui_extra_networks_checkpoints.py | 2 +- 4 files changed, 63 insertions(+), 64 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a9090ffc..c02db232a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Change Log for SD.Next -## Update for 2026-02-07 +## Update for 2026-02-09 - **Upscalers** - add support for [spandrel](https://github.com/chaiNNer-org/spandrel) @@ -15,8 +15,13 @@ - ui: **themes** add *CTD-NT64Light* and *CTD-NT64Dark*, thanks @resonantsky - ui: **gallery** add option to auto-refresh gallery, thanks @awsr - **Internal** + - refactor: nunchaku models are now listed in networks tab as reference models + instead of being used implicitly via quantization, thanks @CalamitousFelicitousness - refactor: reorganize `cli` scripts + - update `lint` rules, thanks @awsr - **Fixes** + - fix: handle `clip` installer doing unwanted `setuptools` update + - fix: cleanup for `uv` installer fallback - fix: add metadata restore to always-on scripts - fix: improve wildcard weights parsing, thanks @Tillerz - fix: ui gallery cace recursive cleanup, thanks @awsr diff --git a/cli/benchmark_attention.py b/cli/benchmark_attention.py index dc20eb1f0..39b0be5b0 100644 --- a/cli/benchmark_attention.py +++ b/cli/benchmark_attention.py @@ -1,18 +1,25 @@ +from typing import Dict, Any import time import warnings import torch import torch.nn.functional as F -from typing import Dict, Any warnings.filterwarnings("ignore", category=UserWarning) warmup = 2 repeats = 50 dtypes = [torch.bfloat16] # , torch.float16] -# if hasattr(torch, "float8_e4m3fn"): -# dtypes.append(torch.float8_e4m3fn) - -PROFILES = { +backends = [ + "sdpa_math", + "sdpa_mem_efficient", + "sdpa_flash", + "sdpa_all", + "flex_attention", + "xformers", + "flash_attn", + "sage_attn", +] +profiles = { "sdxl": {"l_q": 4096, "l_k": 4096, "h": 32, "d": 128}, "flux.1": {"l_q": 16717, "l_k": 16717, "h": 24, "d": 128}, "sd35": {"l_q": 16538, "l_k": 16538, "h": 24, "d": 128}, @@ -30,19 +37,19 @@ def get_stats(reset: bool = False): m = torch.cuda.max_memory_allocated() t = time.perf_counter() return m / (1024 ** 2), t - + def print_gpu_info(): if not torch.cuda.is_available(): print("GPU: Not available") return - + device = torch.cuda.current_device() props = torch.cuda.get_device_properties(device) total_mem = props.total_memory / (1024**3) free_mem, _ = torch.cuda.mem_get_info(device) free_mem = free_mem / (1024**3) major, minor = torch.cuda.get_device_capability(device) - + print(f"gpu: {torch.cuda.get_device_name(device)}") print(f"vram: total={total_mem:.2f}GB free={free_mem:.2f}GB") print(f"cuda: capability={major}.{minor} version={torch.version.cuda}") @@ -56,11 +63,9 @@ def benchmark_attention( l_k: int = 4096, h: int = 32, d: int = 128, - warmup: int = 10, - repeats: int = 100 ) -> Dict[str, Any]: device = "cuda" if torch.cuda.is_available() else "cpu" - + # Initialize tensors q = torch.randn(b, h, l_q, d, device=device, dtype=torch.float16 if dtype.is_floating_point and dtype.itemsize == 1 else dtype, requires_grad=False).to(dtype) k = torch.randn(b, h, l_k, d, device=device, dtype=torch.float16 if dtype.is_floating_point and dtype.itemsize == 1 else dtype, requires_grad=False).to(dtype) @@ -78,7 +83,7 @@ def benchmark_attention( try: if backend.startswith("sdpa_"): from torch.nn.attention import sdpa_kernel, SDPBackend - sdp_type = backend[len("sdpa_"):] + sdp_type = backend[len("sdpa_"):] # Map friendly names to new SDPA backends backend_map = { "math": [SDPBackend.MATH], @@ -88,21 +93,21 @@ def benchmark_attention( } if sdp_type not in backend_map: raise ValueError(f"Unknown SDPA type: {sdp_type}") - + results["version"] = torch.__version__ - + with sdpa_kernel(backend_map[sdp_type]): # Warmup for _ in range(warmup): _ = F.scaled_dot_product_attention(q, k, v) - + start_mem, start_time = get_stats(True) - + for _ in range(repeats): _ = F.scaled_dot_product_attention(q, k, v) - + end_mem, end_time = get_stats() - + results["latency_ms"] = (end_time - start_time) / repeats * 1000 results["memory_mb"] = end_mem - start_mem @@ -113,17 +118,17 @@ def benchmark_attention( q_fa = q.transpose(1, 2) k_fa = k.transpose(1, 2) v_fa = v.transpose(1, 2) - + for _ in range(warmup): _ = flash_attn_func(q_fa, k_fa, v_fa) - + start_mem, start_time = get_stats(True) - + for _ in range(repeats): _ = flash_attn_func(q_fa, k_fa, v_fa) - + end_mem, end_time = get_stats() - + results["latency_ms"] = (end_time - start_time) / repeats * 1000 results["memory_mb"] = end_mem - start_mem @@ -135,17 +140,17 @@ def benchmark_attention( q_xf = q.transpose(1, 2) k_xf = k.transpose(1, 2) v_xf = v.transpose(1, 2) - + for _ in range(warmup): _ = memory_efficient_attention(q_xf, k_xf, v_xf) - + start_mem, start_time = get_stats(True) - + for _ in range(repeats): _ = memory_efficient_attention(q_xf, k_xf, v_xf) - + end_mem, end_time = get_stats() - + results["latency_ms"] = (end_time - start_time) / repeats * 1000 results["memory_mb"] = end_mem - start_mem @@ -158,18 +163,18 @@ def benchmark_attention( results["version"] = importlib.metadata.version("sageattention") except Exception: results["version"] = getattr(sageattention, "__version__", "N/A") - + # SageAttention expects (B, H, L, D) logic for _ in range(warmup): _ = sageattn(q, k, v) - + start_mem, start_time = get_stats(True) - + for _ in range(repeats): _ = sageattn(q, k, v) - + end_mem, end_time = get_stats() - + results["latency_ms"] = (end_time - start_time) / repeats * 1000 results["memory_mb"] = end_mem - start_mem @@ -179,62 +184,52 @@ def benchmark_attention( # flex_attention requires torch.compile for performance flex_attention_compiled = torch.compile(flex_attention, dynamic=False) - + # Warmup (important to trigger compilation) for _ in range(warmup): _ = flex_attention_compiled(q, k, v) - + start_mem, start_time = get_stats(True) - + for _ in range(repeats): _ = flex_attention_compiled(q, k, v) - + end_mem, end_time = get_stats() - + results["latency_ms"] = (end_time - start_time) / repeats * 1000 results["memory_mb"] = end_mem - start_mem except Exception as e: results["status"] = "fail" results["error"] = str(e)[:49] + print(e) return results -def main(): - backends = [ - "sdpa_math", - "sdpa_mem_efficient", - "sdpa_flash", - "flex_attention", - "xformers", - "flash_attn", - "sage_attn", - ] - +def main(): + all_results = [] print_gpu_info() - print(f'config: warmup={warmup} repeats={repeats} dtypes={dtypes}') - for name, config in PROFILES.items(): + print(f'config: warmup={warmup} repeats={repeats} dtypes={dtypes}') + for name, config in profiles.items(): print(f"profile: {name} (L_q={config['l_q']}, L_k={config['l_k']}, H={config['h']}, D={config['d']})") for dtype in dtypes: print(f" dtype: {dtype}") print(f" {'backend':<20} | {'version':<12} | {'status':<8} | {'latency':<10} | {'memory':<12} | ") for backend in backends: res = benchmark_attention( - backend, - dtype, - l_q=config["l_q"], - l_k=config["l_k"], - h=config["h"], - d=config["d"], - warmup=warmup, - repeats=repeats + backend, + dtype, + l_q=config["l_q"], + l_k=config["l_k"], + h=config["h"], + d=config["d"], ) all_results.append(res) - + latency = f"{res['latency_ms']:.4f} ms" memory = f"{res['memory_mb']:.2f} MB" - + print(f" {res['backend']:<20} | {res['version']:<12} | {res['status']:<8} | {latency:<10} | {memory:<12} | {res['error']}") if __name__ == "__main__": diff --git a/modules/shared.py b/modules/shared.py index 0d606ab2a..41eecd1d7 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -880,7 +880,6 @@ profiler = None import modules.styles prompt_styles = modules.styles.StyleDatabase(opts) reference_models = readfile(os.path.join('data', 'reference.json'), as_type="dict") if opts.extra_network_reference_enable else {} -reference_models.update(readfile(os.path.join('data', 'reference-nunchaku.json'), as_type="dict") if opts.extra_network_reference_enable else {}) cmd_opts.disable_extension_access = (cmd_opts.share or cmd_opts.listen or (cmd_opts.server_name or False)) and not cmd_opts.insecure log.debug('Initializing: devices') diff --git a/modules/ui_extra_networks_checkpoints.py b/modules/ui_extra_networks_checkpoints.py index b179d55c6..65078ef97 100644 --- a/modules/ui_extra_networks_checkpoints.py +++ b/modules/ui_extra_networks_checkpoints.py @@ -88,7 +88,7 @@ class ExtraNetworksPageCheckpoints(ui_extra_networks.ExtraNetworksPage): path = f'{v.get("path", "")}' tag = v.get('tags', '') - if tag == 'nunchaku' and devices.backend != 'cuda': + if tag == 'nunchaku' and (devices.backend != 'cuda' and not shared.cmd_opts.experimental): count['hidden'] += 1 continue if tag in count: From e3ca883cbd814e3d379dbec302dd6ee24285c57d Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Mon, 9 Feb 2026 18:30:18 +0100 Subject: [PATCH 12/19] Revert "Mark public re-exports" --- modules/images.py | 22 ++++------------------ modules/shared.py | 46 ++++++++-------------------------------------- 2 files changed, 12 insertions(+), 56 deletions(-) diff --git a/modules/images.py b/modules/images.py index 64b51401a..5e4c5c9dd 100644 --- a/modules/images.py +++ b/modules/images.py @@ -12,24 +12,10 @@ import piexif import piexif.helper from PIL import Image, PngImagePlugin, ExifTags, ImageDraw from modules import sd_samplers, shared, script_callbacks, errors, paths -from modules.images_grid import ( - image_grid as image_grid, - get_grid_size as get_grid_size, - split_grid as split_grid, - combine_grid as combine_grid, - check_grid_size as check_grid_size, - get_font as get_font, - draw_grid_annotations as draw_grid_annotations, - draw_prompt_matrix as draw_prompt_matrix, - GridAnnotation as GridAnnotation, - Grid as Grid, -) -from modules.images_resize import resize_image as resize_image -from modules.images_namegen import ( - FilenameGenerator as FilenameGenerator, - get_next_sequence_number as get_next_sequence_number, -) -from modules.video import save_video as save_video +from modules.images_grid import image_grid, get_grid_size, split_grid, combine_grid, check_grid_size, get_font, draw_grid_annotations, draw_prompt_matrix, GridAnnotation, Grid # pylint: disable=unused-import +from modules.images_resize import resize_image # pylint: disable=unused-import +from modules.images_namegen import FilenameGenerator, get_next_sequence_number # pylint: disable=unused-import +from modules.video import save_video # pylint: disable=unused-import debug = errors.log.trace if os.environ.get('SD_PATH_DEBUG', None) is not None else lambda *args, **kwargs: None diff --git a/modules/shared.py b/modules/shared.py index 41eecd1d7..d549b9a7a 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -4,53 +4,23 @@ import os import sys import time import contextlib + from enum import Enum from typing import TYPE_CHECKING - import gradio as gr - -from installer import ( - log as log, - print_dict, - console as console, - get_version as get_version, -) - -log.debug("Initializing: shared module") +from installer import log, print_dict, console, get_version # pylint: disable=unused-import +log.debug('Initializing: shared module') import modules.memmon import modules.paths as paths -from modules.json_helpers import ( - readfile as readfile, - writefile as writefile, -) -from modules.shared_helpers import ( - listdir as listdir, - walk_files as walk_files, - html_path as html_path, - html as html, - req as req, - total_tqdm as total_tqdm, -) +from modules.json_helpers import readfile, writefile # pylint: disable=W0611 +from modules.shared_helpers import listdir, walk_files, html_path, html, req, total_tqdm # pylint: disable=W0611 from modules import errors, devices, shared_state, cmd_args, theme, history, files_cache from modules.shared_defaults import get_default_modes -from modules.paths import ( - models_path as models_path, # For compatibility, do not modify from here... - script_path as script_path, - data_path as data_path, - sd_configs_path as sd_configs_path, - sd_default_config as sd_default_config, - sd_model_file as sd_model_file, - default_sd_model_file as default_sd_model_file, - extensions_dir as extensions_dir, - extensions_builtin_dir as extensions_builtin_dir, # ... to here. -) -from modules.memstats import ( - memory_stats, - ram_stats as ram_stats, -) +from modules.paths import models_path, script_path, data_path, sd_configs_path, sd_default_config, sd_model_file, default_sd_model_file, extensions_dir, extensions_builtin_dir # pylint: disable=W0611 +from modules.memstats import memory_stats, ram_stats # pylint: disable=unused-import -log.debug("Initializing: pipelines") +log.debug('Initializing: pipelines') from modules import shared_items from modules.interrogate.openclip import caption_models, caption_types, get_clip_models, refresh_clip_models from modules.interrogate.vqa import vlm_models, vlm_prompts, vlm_system, vlm_default From 42d8ad498eb76d2a86ea8f5376dfd19ceb5fb785 Mon Sep 17 00:00:00 2001 From: vladmandic Date: Mon, 9 Feb 2026 19:54:26 +0100 Subject: [PATCH 13/19] add ftfy Signed-off-by: vladmandic --- CHANGELOG.md | 1 + installer.py | 1 + requirements.txt | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c02db232a..40872d1fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ instead of being used implicitly via quantization, thanks @CalamitousFelicitousness - refactor: reorganize `cli` scripts - update `lint` rules, thanks @awsr + - update `requirements` - **Fixes** - fix: handle `clip` installer doing unwanted `setuptools` update - fix: cleanup for `uv` installer fallback diff --git a/installer.py b/installer.py index 28356e63f..c162047c9 100644 --- a/installer.py +++ b/installer.py @@ -1124,6 +1124,7 @@ def install_packages(): clip_package = os.environ.get('CLIP_PACKAGE', "git+https://github.com/openai/CLIP.git") install(clip_package, 'clip', quiet=True, no_build_isolation=True) install('open-clip-torch', no_deps=True, quiet=True) + install(clip_package, 'ftfy', quiet=True, no_build_isolation=True) # tensorflow_package = os.environ.get('TENSORFLOW_PACKAGE', 'tensorflow==2.13.0') # tensorflow_package = os.environ.get('TENSORFLOW_PACKAGE', None) # if tensorflow_package is not None: diff --git a/requirements.txt b/requirements.txt index 227e20fa4..8aede7efd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -55,7 +55,7 @@ protobuf==4.25.3 pytorch_lightning==2.6.0 urllib3==1.26.19 Pillow==10.4.0 -timm==1.0.16 +timm==1.0.24 pyparsing==3.2.3 typing-extensions==4.14.1 sentencepiece==0.2.1 From 363cb175aa62295e5a9fdea60da987c2abe53f84 Mon Sep 17 00:00:00 2001 From: vladmandic Date: Mon, 9 Feb 2026 22:31:00 +0100 Subject: [PATCH 14/19] allow different lora in hires Signed-off-by: vladmandic --- CHANGELOG.md | 1 + modules/extra_networks.py | 9 ++++++--- modules/processing.py | 1 - modules/processing_diffusers.py | 16 +++++++++++++--- 4 files changed, 20 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 40872d1fc..d151c3b72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - 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 + - hires: allow using different lora in refiner prompt - **UI** - ui: **themes** add *CTD-NT64Light* and *CTD-NT64Dark*, thanks @resonantsky - ui: **gallery** add option to auto-refresh gallery, thanks @awsr diff --git a/modules/extra_networks.py b/modules/extra_networks.py index eab3ab7ad..01913b187 100644 --- a/modules/extra_networks.py +++ b/modules/extra_networks.py @@ -156,7 +156,6 @@ def parse_prompt(prompt: str | None) -> tuple[str, defaultdict[str, list[ExtraNe if prompt is None: return "", res if isinstance(prompt, list): - shared.log.warning(f"parse_prompt was called with a list instead of a string: {prompt}") return parse_prompts(prompt) def found(m: re.Match[str]): @@ -168,13 +167,17 @@ def parse_prompt(prompt: str | None) -> tuple[str, defaultdict[str, list[ExtraNe return updated_prompt, res -def parse_prompts(prompts: list[str]): +def parse_prompts(prompts: list[str], extra_data=None): updated_prompt_list: list[str] = [] - extra_data: defaultdict[str, list[ExtraNetworkParams]] = defaultdict(list) + extra_data: defaultdict[str, list[ExtraNetworkParams]] = extra_data or defaultdict(list) for prompt in prompts: updated_prompt, parsed_extra_data = parse_prompt(prompt) if not extra_data: extra_data = parsed_extra_data + elif parsed_extra_data: + extra_data = parsed_extra_data + else: + pass updated_prompt_list.append(updated_prompt) return updated_prompt_list, extra_data diff --git a/modules/processing.py b/modules/processing.py index d9579047b..32bbf3545 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -272,7 +272,6 @@ def process_init(p: StableDiffusionProcessing): p.all_prompts, p.all_negative_prompts = shared.prompt_styles.apply_styles_to_prompts(p.all_prompts, p.all_negative_prompts, p.styles, p.all_seeds) p.prompts = p.all_prompts[(p.iteration * p.batch_size):((p.iteration+1) * p.batch_size)] p.negative_prompts = p.all_negative_prompts[(p.iteration * p.batch_size):((p.iteration+1) * p.batch_size)] - p.prompts, _ = extra_networks.parse_prompts(p.prompts) def process_samples(p: StableDiffusionProcessing, samples): diff --git a/modules/processing_diffusers.py b/modules/processing_diffusers.py index a410497e2..04ae68ea2 100644 --- a/modules/processing_diffusers.py +++ b/modules/processing_diffusers.py @@ -171,6 +171,7 @@ def process_base(p: processing.StableDiffusionProcessing): modelstats.analyze() try: t0 = time.time() + p.prompts, p.network_data = extra_networks.parse_prompts(p.prompts, p.network_data) extra_networks.activate(p, exclude=['text_encoder', 'text_encoder_2', 'text_encoder_3']) if hasattr(shared.sd_model, 'tgate') and getattr(p, 'gate_step', -1) > 0: @@ -297,10 +298,20 @@ def process_hires(p: processing.StableDiffusionProcessing, output): p.denoising_strength = strength orig_image = p.task_args.pop('image', None) # remove image override from hires process_pre(p) + + prompts = p.prompts + reset_prompts = False + if len(p.refiner_prompt) > 0: + prompts = len(output.images)* [p.refiner_prompt] + prompts, p.network_data = extra_networks.parse_prompts(prompts) + reset_prompts = True + if reset_prompts or ('base' in p.skip): + extra_networks.activate(p) + hires_args = set_pipeline_args( p=p, model=shared.sd_model, - prompts=len(output.images)* [p.refiner_prompt] if len(p.refiner_prompt) > 0 else p.prompts, + prompts=prompts, negative_prompts=len(output.images) * [p.refiner_negative] if len(p.refiner_negative) > 0 else p.negative_prompts, prompts_2=len(output.images) * [p.refiner_prompt] if len(p.refiner_prompt) > 0 else p.prompts, negative_prompts_2=len(output.images) * [p.refiner_negative] if len(p.refiner_negative) > 0 else p.negative_prompts, @@ -314,11 +325,10 @@ def process_hires(p: processing.StableDiffusionProcessing, output): strength=strength, desc='Hires', ) + hires_steps = hires_args.get('prior_num_inference_steps', None) or p.hr_second_pass_steps or hires_args.get('num_inference_steps', None) shared.state.update(get_job_name(p, shared.sd_model), hires_steps, 1) try: - if 'base' in p.skip: - extra_networks.activate(p) taskid = shared.state.begin('Inference') output = shared.sd_model(**hires_args) # pylint: disable=not-callable shared.state.end(taskid) From e907a0a5734ec8431dc9675585d4c3ab66d99475 Mon Sep 17 00:00:00 2001 From: vladmandic Date: Mon, 9 Feb 2026 22:46:32 +0100 Subject: [PATCH 15/19] update graphics Signed-off-by: vladmandic --- html/logo-dark.svg | 7 +++++ html/logo-light.svg | 7 +++++ html/logo-margins.png | Bin 17223 -> 0 bytes html/logo-robot.svg | 19 +++++++++++++ html/logo-wm.png | Bin 14328 -> 0 bytes html/logo.svg | 62 ------------------------------------------ 6 files changed, 33 insertions(+), 62 deletions(-) create mode 100644 html/logo-dark.svg create mode 100644 html/logo-light.svg delete mode 100644 html/logo-margins.png create mode 100644 html/logo-robot.svg delete mode 100644 html/logo-wm.png delete mode 100644 html/logo.svg diff --git a/html/logo-dark.svg b/html/logo-dark.svg new file mode 100644 index 000000000..9a32c4a44 --- /dev/null +++ b/html/logo-dark.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/html/logo-light.svg b/html/logo-light.svg new file mode 100644 index 000000000..c4e519862 --- /dev/null +++ b/html/logo-light.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/html/logo-margins.png b/html/logo-margins.png deleted file mode 100644 index 5bbc3f2528970625da970b3fe3726b6101ec57c5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17223 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 KZj~$BM*bg^-vBEB diff --git a/html/logo-robot.svg b/html/logo-robot.svg new file mode 100644 index 000000000..7a4791f6c --- /dev/null +++ b/html/logo-robot.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/html/logo-wm.png b/html/logo-wm.png deleted file mode 100644 index 1bfdbf53ae3f953bf1c9db47ba54300703788d74..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14328 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 mvm5!8p3+l#O8 - - - From 684d77d871439ea0e8d580bc5b232016b08b88d7 Mon Sep 17 00:00:00 2001 From: vladmandic Date: Tue, 10 Feb 2026 11:49:01 +0100 Subject: [PATCH 16/19] update diffusers Signed-off-by: vladmandic --- installer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installer.py b/installer.py index c162047c9..0e38b1daf 100644 --- a/installer.py +++ b/installer.py @@ -687,7 +687,7 @@ def check_diffusers(): t_start = time.time() if args.skip_all: return - sha = '99e2cfff27dec514a43e260e885c5e6eca038b36' # diffusers commit hash + sha = '5bf248ddd8796b4f4958559429071a28f9b2dd3a' # diffusers commit hash # if args.use_rocm or args.use_zluda or args.use_directml: # sha = '043ab2520f6a19fce78e6e060a68dbc947edb9f9' # lock diffusers versions for now pkg = pkg_resources.working_set.by_key.get('diffusers', None) From bd61633e1496da4a00b344c3c59409b28bb5cd5a Mon Sep 17 00:00:00 2001 From: vladmandic Date: Tue, 10 Feb 2026 13:51:51 +0100 Subject: [PATCH 17/19] switch to pyproject.toml for tool config Signed-off-by: vladmandic --- .pylintrc | 284 ----------------------------- .ruff.toml | 109 ----------- CHANGELOG.md | 3 +- cli/test-weighted-lists.py | 25 +-- pyproject.toml | 362 +++++++++++++++++++++++++++++++++++++ 5 files changed, 377 insertions(+), 406 deletions(-) delete mode 100644 .pylintrc delete mode 100644 .ruff.toml create mode 100644 pyproject.toml diff --git a/.pylintrc b/.pylintrc deleted file mode 100644 index e4b0a6b4b..000000000 --- a/.pylintrc +++ /dev/null @@ -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*(# )??$ -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 diff --git a/.ruff.toml b/.ruff.toml deleted file mode 100644 index 443556400..000000000 --- a/.ruff.toml +++ /dev/null @@ -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", - "TC", - "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 diff --git a/CHANGELOG.md b/CHANGELOG.md index d151c3b72..8e2556f28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,8 @@ - ui: **themes** add *CTD-NT64Light* and *CTD-NT64Dark*, thanks @resonantsky - ui: **gallery** add option to auto-refresh gallery, thanks @awsr - **Internal** - - refactor: nunchaku models are now listed in networks tab as reference models + - refactor: switch to `pyproject.toml` for tool configs + - refactor: **nunchaku** models are now listed in networks tab as reference models instead of being used implicitly via quantization, thanks @CalamitousFelicitousness - refactor: reorganize `cli` scripts - update `lint` rules, thanks @awsr diff --git a/cli/test-weighted-lists.py b/cli/test-weighted-lists.py index 439f08a15..38ceb0e41 100644 --- a/cli/test-weighted-lists.py +++ b/cli/test-weighted-lists.py @@ -1,6 +1,7 @@ #!/usr/bin/env python -import sys, os +import sys +import os from collections import Counter script_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -20,25 +21,25 @@ tolerance_pct = 5 # tests tests = [ # - empty - ["", { '': 100 } ], + ["", { '': 100 } ], # - no weights - [ "red|blonde|black", { 'black': 33, 'red': 33, 'blonde': 33 } ], + [ "red|blonde|black", { 'black': 33, 'red': 33, 'blonde': 33 } ], # - full weights <= 1 - [ "red:0.1|blonde:0.9", { 'blonde': 90, 'red': 10 } ], + [ "red:0.1|blonde:0.9", { 'blonde': 90, 'red': 10 } ], # - weights > 1 to test normalization - [ "red:1|blonde:2|black:5", { 'blonde': 25, 'red': 12.5, 'black': 62.5 } ], + [ "red:1|blonde:2|black:5", { 'blonde': 25, 'red': 12.5, 'black': 62.5 } ], # - disabling 0 weights to force one result - [ "red:0|blonde|black:0", { 'blonde': 100 } ], + [ "red:0|blonde|black:0", { 'blonde': 100 } ], # - weights <= 1 with distribution of the leftover - [ "red:0.5|blonde|black:0.3|brown", { 'red': 50, 'black': 30, 'brown': 10, 'blonde': 10 } ], + [ "red:0.5|blonde|black:0.3|brown", { 'red': 50, 'black': 30, 'brown': 10, 'blonde': 10 } ], # - weights > 1, unweightes should get default of 1 - [ "red:2|blonde|black", { 'red': 50, 'blonde': 25, 'black': 25 } ], + [ "red:2|blonde|black", { 'red': 50, 'blonde': 25, 'black': 25 } ], # - ignore content of () - [ "red:0.5|(blonde:1.3)", { 'red': 50, '(blonde:1.3)': 50 } ], + [ "red:0.5|(blonde:1.3)", { 'red': 50, '(blonde:1.3)': 50 } ], # - ignore content of [] - [ "red:0.5|[stuff:1.3]", { '[stuff:1.3]': 50, 'red': 50 } ], + [ "red:0.5|[stuff:1.3]", { '[stuff:1.3]': 50, 'red': 50 } ], # - ignore content of <> - [ "red:0.5|", { '': 50, 'red': 50 } ] + [ "red:0.5|", { '': 50, 'red': 50 } ] ] # ------------------------------------------------- @@ -109,4 +110,4 @@ with open(fn, 'r', encoding='utf-8') as f: print("RESULT: FAILED (distribution)") else: print("RESULT: PASSED") - print('') \ No newline at end of file + print('') diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 000000000..372c6fad9 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,362 @@ +[project] +name = "SD.Next" +version = "0.0.0" +description = "SD.Next: All-in-one WebUI for AI generative image and video creation and captioning" +readme = "README.md" +requires-python = ">=3.10" + +[tool.ruff] +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", +] + +[tool.ruff.lint] +select = [ + "F", + "E", + "W", + "C", + "B", + "I", + "YTT", + "ASYNC", + "RUF", + "AIR", + "NPY", + "C4", + "T10", + "EXE", + "ISC", + "ICN", + "RSE", + "TC", + "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]+?))$" + +[tool.ruff.format] +quote-style = "double" +indent-style = "space" +skip-magic-trailing-comma = false +line-ending = "auto" +docstring-code-format = false + +[tool.ruff.lint.mccabe] +max-complexity = 150 + +[tool.pylint] +main.py-version="3.10" +main.analyse-fallback-blocks=false +main.clear-cache-post-run=false +main.extension-pkg-allow-list="" +main.prefer-stubs=true +main.extension-pkg-whitelist="" +main.fail-on="" +main.fail-under=10 +main.ignore="CVS" +main.ignore-paths=[ + "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", + ] +main.ignore-patterns=[ + ".*test*.py$", + ".*_model.py$", + ".*_arch.py$", + ".*_model_arch.py*", + ".*_model_arch_v2.py$", + ] +main.ignored-modules="" +main.jobs=8 +main.limit-inference-results=100 +main.load-plugins="" +main.persistent=false +main.recursive=false +main.source-roots="" +main.unsafe-load-any-extension=false +basic.argument-naming-style="snake_case" +basic.attr-naming-style="snake_case" +basic.bad-names=["foo", "bar", "baz", "toto", "tutu", "tata"] +basic.bad-names-rgxs="" +basic.class-attribute-naming-style="any" +basic.class-const-naming-style="UPPER_CASE" +basic.class-naming-style="PascalCase" +basic.const-naming-style="snake_case" +basic.docstring-min-length=-1 +basic.function-naming-style="snake_case" +basic.good-names=["i","j","k","e","ex","ok","p","x","y","id"] +basic.good-names-rgxs="" +basic.include-naming-hint=false +basic.inlinevar-naming-style="any" +basic.method-naming-style="snake_case" +basic.module-naming-style="snake_case" +basic.name-group="" +basic.no-docstring-rgx="^_" +basic.property-classes="abc.abstractproperty" +basic.variable-naming-style="snake_case" +classes.check-protected-access-in-special-methods=false +classes.defining-attr-methods=["__init__", "__new__"] +classes.exclude-protected=["_asdict","_fields","_replace","_source","_make","os._exit"] +classes.valid-classmethod-first-arg="cls" +classes.valid-metaclass-classmethod-first-arg="mcs" +design.exclude-too-few-public-methods="" +design.ignored-parents="" +design.max-args=199 +design.max-attributes=99 +design.max-bool-expr=99 +design.max-branches=199 +design.max-locals=99 +design.max-parents=99 +design.max-public-methods=99 +design.max-returns=99 +design.max-statements=199 +design.min-public-methods=1 +exceptions.overgeneral-exceptions=["builtins.BaseException","builtins.Exception"] +format.expected-line-ending-format="" +# format.ignore-long-lines="^\s*(# )??$" +format.indent-after-paren=4 +format.indent-string=' ' +format.max-line-length=200 +format.max-module-lines=9999 +format.single-line-class-stmt=false +format.single-line-if-stmt=false +imports.allow-any-import-level="" +imports.allow-reexport-from-package=false +imports.allow-wildcard-with-all=false +imports.deprecated-modules="" +imports.ext-import-graph="" +imports.import-graph="" +imports.int-import-graph="" +imports.known-standard-library="" +imports.known-third-party="enchant" +imports.preferred-modules="" +logging.logging-format-style="new" +logging.logging-modules="logging" +messages_control.confidence=["HIGH","CONTROL_FLOW","INFERENCE","INFERENCE_FAILURE","UNDEFINED"] +messages_control.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", + ] +messages_control.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"] +miscellaneous.notes-rgx="" +refactoring.max-nested-blocks=5 +refactoring.never-returning-functions=["sys.exit","argparse.parse_error"] +reports.msg-template="" +reports.reports=false +reports.score=false +similarities.ignore-comments=true +similarities.ignore-docstrings=true +similarities.ignore-imports=true +similarities.ignore-signatures=true +similarities.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=false +string.check-quote-consistency=false +string.check-str-concat-over-line-jumps=false +typecheck.contextmanager-decorators="contextlib.contextmanager" +typecheck.generated-members=["numpy.*","logging.*","torch.*","cv2.*"] +typecheck.ignore-none=true +typecheck.ignore-on-opaque-inference=true +typecheck.ignored-checks-for-mixins=["no-member","not-async-context-manager","not-context-manager","attribute-defined-outside-init"] +typecheck.ignored-classes=["optparse.Values","thread._local","_thread._local","argparse.Namespace","unittest.case._AssertRaisesContext","unittest.case._AssertWarnsContext"] +typecheck.missing-member-hint=true +typecheck.missing-member-hint-distance=1 +typecheck.missing-member-max-choices=1 +typecheck.mixin-class-rgx=".*[Mm]ixin" +typecheck.signature-mutators="" +variables.additional-builtins="" +variables.allow-global-unused-variables=true +variables.allowed-redefined-builtins="" +variables.callbacks=["cb_",] +variables.dummy-variables-rgx="_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_" +variables.ignored-argument-names="_.*|^ignored_|^unused_" +variables.init-import=false +variables.redefining-builtins-modules=["six.moves","past.builtins","future.builtins","builtins","io"] From d602a093fbb62f844470d6c9f7d37a03e820af67 Mon Sep 17 00:00:00 2001 From: vladmandic Date: Tue, 10 Feb 2026 13:54:13 +0100 Subject: [PATCH 18/19] lint Signed-off-by: vladmandic --- .github/ISSUE_TEMPLATE/model_request | 8 ++++---- html/logo-dark.svg | 2 +- html/logo-light.svg | 2 +- html/logo-robot.svg | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/model_request b/.github/ISSUE_TEMPLATE/model_request index adf8d10f8..72afc26ce 100644 --- a/.github/ISSUE_TEMPLATE/model_request +++ b/.github/ISSUE_TEMPLATE/model_request @@ -9,22 +9,22 @@ body: attributes: label: Model name description: Enter model name - value: + value: - type: textarea id: type attributes: label: Model type description: Describe model type - value: + value: - type: textarea id: url attributes: label: Model URL description: Enter URL to the model page - value: + value: - type: textarea id: description attributes: label: Reason description: Enter a reason why you would like to see this model supported - value: + value: diff --git a/html/logo-dark.svg b/html/logo-dark.svg index 9a32c4a44..c949d8847 100644 --- a/html/logo-dark.svg +++ b/html/logo-dark.svg @@ -4,4 +4,4 @@ - \ No newline at end of file + diff --git a/html/logo-light.svg b/html/logo-light.svg index c4e519862..62c70dd2a 100644 --- a/html/logo-light.svg +++ b/html/logo-light.svg @@ -4,4 +4,4 @@ - \ No newline at end of file + diff --git a/html/logo-robot.svg b/html/logo-robot.svg index 7a4791f6c..52d86b16d 100644 --- a/html/logo-robot.svg +++ b/html/logo-robot.svg @@ -16,4 +16,4 @@ - \ No newline at end of file + From 7eb9b1cc5cf92912c5f5f6577ba788534e637523 Mon Sep 17 00:00:00 2001 From: vladmandic Date: Tue, 10 Feb 2026 14:31:53 +0100 Subject: [PATCH 19/19] create tests folder Signed-off-by: vladmandic --- CHANGELOG.md | 5 +++-- {cli => test}/benchmark_attention.py | 0 {cli => test}/full-test.sh | 0 {cli => test}/locale-sanitize-override.py | 0 {cli => test}/localize.js | 0 {cli => test}/test-schedulers.py | 0 {cli => test}/test-tagger.py | 0 {cli => test}/test-weighted-lists.py | 0 {cli => test}/validate-locale.py | 0 9 files changed, 3 insertions(+), 2 deletions(-) rename {cli => test}/benchmark_attention.py (100%) rename {cli => test}/full-test.sh (100%) mode change 100755 => 100644 rename {cli => test}/locale-sanitize-override.py (100%) mode change 100755 => 100644 rename {cli => test}/localize.js (100%) mode change 100755 => 100644 rename {cli => test}/test-schedulers.py (100%) rename {cli => test}/test-tagger.py (100%) rename {cli => test}/test-weighted-lists.py (100%) rename {cli => test}/validate-locale.py (100%) mode change 100755 => 100644 diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e2556f28..96d74dc25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,14 +12,15 @@ - add `--remote` command line flag that reduces client/server chatter and improves link stability for long-running generates, useful when running on remote servers - 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, thanks @CalamitousFelicitousness - **UI** - ui: **themes** add *CTD-NT64Light* and *CTD-NT64Dark*, thanks @resonantsky - ui: **gallery** add option to auto-refresh gallery, thanks @awsr - **Internal** - refactor: switch to `pyproject.toml` for tool configs - - refactor: **nunchaku** models are now listed in networks tab as reference models - instead of being used implicitly via quantization, thanks @CalamitousFelicitousness - refactor: reorganize `cli` scripts + - refactor: move tests to dedicated `/test/` - update `lint` rules, thanks @awsr - update `requirements` - **Fixes** diff --git a/cli/benchmark_attention.py b/test/benchmark_attention.py similarity index 100% rename from cli/benchmark_attention.py rename to test/benchmark_attention.py diff --git a/cli/full-test.sh b/test/full-test.sh old mode 100755 new mode 100644 similarity index 100% rename from cli/full-test.sh rename to test/full-test.sh diff --git a/cli/locale-sanitize-override.py b/test/locale-sanitize-override.py old mode 100755 new mode 100644 similarity index 100% rename from cli/locale-sanitize-override.py rename to test/locale-sanitize-override.py diff --git a/cli/localize.js b/test/localize.js old mode 100755 new mode 100644 similarity index 100% rename from cli/localize.js rename to test/localize.js diff --git a/cli/test-schedulers.py b/test/test-schedulers.py similarity index 100% rename from cli/test-schedulers.py rename to test/test-schedulers.py diff --git a/cli/test-tagger.py b/test/test-tagger.py similarity index 100% rename from cli/test-tagger.py rename to test/test-tagger.py diff --git a/cli/test-weighted-lists.py b/test/test-weighted-lists.py similarity index 100% rename from cli/test-weighted-lists.py rename to test/test-weighted-lists.py diff --git a/cli/validate-locale.py b/test/validate-locale.py old mode 100755 new mode 100644 similarity index 100% rename from cli/validate-locale.py rename to test/validate-locale.py