fix ansi controle output and add model locking

Signed-off-by: Vladimir Mandic <mandic00@live.com>
pull/4039/head
Vladimir Mandic 2025-07-06 12:50:09 -04:00
parent 8dc5dac308
commit b2a52a146b
9 changed files with 49 additions and 14 deletions

View File

@ -12,7 +12,6 @@
*relighting*: automatic background replacement with reglighting so source image fits desired background
with optional composite blending
available in *img2img or control -> scripts*
- **TAESD** is now default preview type since its the only one that supports most new models
- Add **FLUX.1-Kontext-Dev** inpaint workflow
- Support **FLUX.1** all-in-one safetensors
- Support TAESD preview and remote VAE for **HunyuanDit**
@ -25,14 +24,22 @@
enable in *settings -> compute settings -> sdp options*
*note*: SD.Next will use either SageAttention v1/v2/v2++, depending which one is installed
until authors provide pre-build wheels for v2++, you need to install it manually or SD.Next will auto-install v1
- **Other**
- **TAESD** is now default preview type since its the only one that supports most new models
- SD.Next now starts with *locked* state preventing model loading until startup is complete
- **API**
- add `/sdapi/v1/lock-checkpoint` endpoint that can be used to lock/unlock model changes
if model is locked, it cannot be changed using normal load or unload methods
- **Fixes**
- allow theme type `None` to be set in config
- installer dont cache installed state
- fix Cosmos-Predict2 retrying TAESD download
- better handle startup import errors
- fix ansi controle output from scripts/extensions
- fix diffusers models non-unique hash
- fix loading of manually downloaded diffuser models
- fix api `/sdapi/v1/embeddings` endpoint
- improve extensions ui search
- improve model type autodetection
- improve model auth check for hf repos
- improve Chroma prompt padding as per recommendations

View File

@ -94,6 +94,7 @@ class Api:
self.add_api_route("/sdapi/v1/refresh-checkpoints", endpoints.post_refresh_checkpoints, methods=["POST"])
self.add_api_route("/sdapi/v1/unload-checkpoint", endpoints.post_unload_checkpoint, methods=["POST"])
self.add_api_route("/sdapi/v1/reload-checkpoint", endpoints.post_reload_checkpoint, methods=["POST"])
self.add_api_route("/sdapi/v1/lock-checkpoint", endpoints.post_lock_checkpoint, methods=["POST"])
self.add_api_route("/sdapi/v1/refresh-vae", endpoints.post_refresh_vae, methods=["POST"])
self.add_api_route("/sdapi/v1/latents", endpoints.get_latent_history, methods=["GET"], response_model=List[str])
self.add_api_route("/sdapi/v1/latents", endpoints.post_latent_history, methods=["POST"], response_model=int)

View File

@ -115,6 +115,11 @@ def post_reload_checkpoint(force:bool=False):
sd_models.reload_model_weights()
return {}
def post_lock_checkpoint(lock:bool=False):
from modules import modeldata
modeldata.model_data.locked = lock
return {}
def get_checkpoint():
if not shared.sd_loaded or shared.sd_model is None:
checkpoint = {

View File

@ -65,7 +65,8 @@ def main_args():
def compatibility_args():
# removed args are added here as hidden in fixed format for compatbility reasons
group_compat = parser.add_argument_group('Compatibility options')
group_compat.add_argument('--backend', type=str, default=os.environ.get("SD_BACKEND", None), choices=['diffusers', 'original'], required=False, help='obsolete')
group_compat.add_argument('--backend', type=str, choices=['diffusers', 'original'], help=argparse.SUPPRESS)
group_compat.add_argument('--hypernetwork-dir', default='.', help=argparse.SUPPRESS)
group_compat.add_argument("--allow-code", default=os.environ.get("SD_ALLOWCODE", False), action='store_true', help=argparse.SUPPRESS)
group_compat.add_argument("--enable_insecure_extension_access", default=os.environ.get("SD_INSECURE", False), action='store_true', help=argparse.SUPPRESS)
group_compat.add_argument("--use-cpu", nargs='+', default=[], type=str.lower, help=argparse.SUPPRESS)

View File

@ -0,0 +1,3 @@
# a1111 compatibility module: unused
from modules.infotext import parse as parse_generation_parameters # pylint: disable=unused-import

View File

@ -1,3 +1,4 @@
import os
import sys
import threading
from modules import shared, errors
@ -77,15 +78,20 @@ class ModelData:
self.sd_refiner = None
self.sd_dict = 'None'
self.initial = True
self.locked = True
self.lock = threading.Lock()
def get_sd_model(self):
from modules.sd_models import reload_model_weights
if self.sd_model is None and shared.opts.sd_model_checkpoint != 'None' and not self.lock.locked():
if self.locked:
if self.sd_model is None:
fn = f'{os.path.basename(sys._getframe(2).f_code.co_filename)}:{sys._getframe(2).f_code.co_name}:{sys._getframe(1).f_code.co_name}' # pylint: disable=protected-access
shared.log.warning(f'Model locked: fn={fn}')
return self.sd_model
elif (self.sd_model is None) and (shared.opts.sd_model_checkpoint != 'None') and (not self.lock.locked()):
with self.lock:
try:
# note: reload_model_weights directly updates model_data.sd_model and returns it at the end
self.sd_model = reload_model_weights(op='model')
from modules.sd_models import reload_model_weights
self.sd_model = reload_model_weights(op='model') # note: reload_model_weights directly updates model_data.sd_model and returns it at the end
self.initial = False
except Exception as e:
shared.log.error("Failed to load stable diffusion model")
@ -94,13 +100,14 @@ class ModelData:
return self.sd_model
def set_sd_model(self, v):
self.sd_model = v
if not self.locked:
self.sd_model = v
def get_sd_refiner(self):
from modules.sd_models import reload_model_weights
if self.sd_refiner is None and shared.opts.sd_model_refiner != 'None' and not self.lock.locked():
if (self.sd_refiner is None) and (shared.opts.sd_model_refiner != 'None') and (not self.lock.locked()):
with self.lock:
try:
from modules.sd_models import reload_model_weights
self.sd_refiner = reload_model_weights(op='refiner')
self.initial = False
except Exception as e:
@ -110,7 +117,8 @@ class ModelData:
return self.sd_refiner
def set_sd_refiner(self, v):
self.sd_refiner = v
if not self.locked:
self.sd_refiner = v
# provides shared.sd_model field as a property
@ -124,7 +132,7 @@ class Shared(sys.modules[__name__].__class__):
def sd_model(self):
import modules.sd_models # pylint: disable=W0621
if modules.sd_models.model_data.sd_model is None:
fn = f'{sys._getframe(2).f_code.co_name}:{sys._getframe(1).f_code.co_name}' # pylint: disable=protected-access
fn = f'{os.path.basename(sys._getframe(2).f_code.co_filename)}:{sys._getframe(2).f_code.co_name}:{sys._getframe(1).f_code.co_name}' # pylint: disable=protected-access
shared.log.debug(f'Model requested: fn={fn}') # pylint: disable=protected-access
return modules.sd_models.model_data.get_sd_model()

View File

@ -26,7 +26,10 @@ def load_module(path):
setup_logging() # reset since scripts can hijaack logging
for line in stdout.getvalue().splitlines():
if len(line) > 0:
errors.log.info(f"Extension: script='{os.path.relpath(path)}' {line.strip()}")
if '2;36m' in line: # color escape sequence
print(line.strip())
else:
errors.log.info(f"Extension: script='{os.path.relpath(path)}' {line.strip()}")
except Exception as e:
errors.display(e, f'Module load: {path}')
return module

View File

@ -354,7 +354,13 @@ def create_html(search_text, sort_column):
visible = 'table-row'
if search_text:
s = search_text.strip().lower()
if s not in html.escape(ext.get("name", "unknown")).lower() and s not in html.escape(ext.get("description", "")).lower() and s not in html.escape(tags_string).lower() and s not in author.lower():
if (
s not in html.escape(ext.get("name", "unknown")).lower()
and s not in html.escape(ext.get("description", "")).lower()
and s not in html.escape(ext.get("url", "")).lower()
and s not in html.escape(tags_string).lower()
and s not in author.lower()
):
stats['hidden'] += 1
visible = 'none'
stats['processed'] += 1

View File

@ -13,7 +13,7 @@ import modules.loader
import modules.hashes
from installer import log, git_commit, custom_excepthook
from modules import timer, paths, shared, extensions, gr_tempdir, modelloader
from modules import timer, paths, shared, extensions, gr_tempdir, modelloader, modeldata
from modules.call_queue import queue_lock, wrap_queued_call, wrap_gradio_gpu_call # pylint: disable=unused-import
import modules.devices
import modules.sd_checkpoint
@ -146,6 +146,7 @@ def initialize():
def load_model():
modeldata.model_data.locked = False
if not shared.opts.sd_checkpoint_autoload and shared.cmd_opts.ckpt is None:
log.info('Model: autoload=False')
else: