diff --git a/CHANGELOG.md b/CHANGELOG.md index 3831f2fc1..a91ad2e17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,23 +34,24 @@ Plus new **Krea 2** model and **SDNQ** improvements: now with *NPU* support and - **Internal** - delay init of video models - **Fixes** - - `sdnq` warn instead of error for triton - - `insightface` missing dependencies - - `pulid` import paths - - `processors` init code and multiple fixes - - `live preview` configurable pause when not in focus, thanks @Artheriax - - `mps` install `torchsde` as requirement - - `vae` scale factor improved detection - - `ui` networks details scrollbars - - `onnxruntime` handle invalid version - - `onnxruntime` mark all import paths as non-critical - - `measure` handle current kanvas stage - - `caption` button in standard-ui - - `python` experimental/ignore version checks - - `hipBLASLt` improved detection, thanks @0xDELUXA - - `embeddings` handle textual-inversion with new transformers - - `options` handle compatibility options - - `log` strip ansi sequences from ring buffer and client side logging + - sdnq: warn instead of error for triton + - insightface: missing dependencies + - pulid: import paths + - processors: init code and multiple fixes + - live preview: configurable pause when not in focus, thanks @Artheriax + - mps: install `torchsde` as requirement + - vae: scale factor improved detection + - ui: networks details scrollbars + - onnxruntime: handle invalid version + - onnxruntime: mark all import paths as non-critical + - measure: handle current kanvas stage + - caption: button in standard-ui + - python: experimental/ignore version checks + - hipBLASLt: improved detection, thanks @0xDELUXA + - embeddings: handle textual-inversion with new transformers + - options: handle compatibility options + - log: strip ansi sequences from ring buffer and client side logging + - model metadata: handle invalid metadata and strip workflows ## Update for 2026-06-16 diff --git a/installer.py b/installer.py index 696011bb0..f4fba4e5d 100644 --- a/installer.py +++ b/installer.py @@ -538,7 +538,7 @@ def check_diffusers(): t_start = time.time() if args.skip_all: return - target_commit = "9d52996c4dd46a1a075d2a8fa32432857a6705e5" # diffusers commit hash == 0.39.0.dev0 == 06-15-2026 + target_commit = "ea802951f5fb235b6af8fe9247f56187d49748b2" # diffusers commit hash == 0.39.0.dev0 == 06-29-2026 # if args.use_rocm or args.use_zluda or args.use_directml: # sha = '043ab2520f6a19fce78e6e060a68dbc947edb9f9' # lock diffusers versions for now pkg = package_spec('diffusers') @@ -568,7 +568,7 @@ def check_transformers(): pkg_tokenizers = package_spec('tokenizers') # target_commit = '753d61104116eefc8ffc977327b441ee0c8d599f' # transformers commit hash == 4.57.6 # target_commit = "380e3cc5d59912a48508cb6d4959a31cd460e12e" # transformers commit hash == 5.5.0.dev-0409 - target_commit = "d242bb790bcbbe6c9a20e46cf9d70648739a90bf" # transformers commit hash == 5.13.0.dev0 == 06-15-2026 + target_commit = "d9e7791b129797be80ca7b3b0bf2a53cda0d4b8c" # transformers commit hash == 5.13.0.dev0 == 06-29-2026 if args.use_directml: target_transformers = '4.52.4' target_tokenizers = '0.21.4' diff --git a/modules/json_helpers.py b/modules/json_helpers.py index 46e19e9ed..4e6c5551a 100644 --- a/modules/json_helpers.py +++ b/modules/json_helpers.py @@ -39,6 +39,8 @@ def readfile(filename: str | os.PathLike[str], silent: bool = False, lock: bool t0 = time.time() with open(filename, "rb") as file: b = file.read() + if len(b) == 0: + return {} if as_type == "dict" else [] data = orjson.loads(b) # pylint: disable=no-member # if type(data) is str: # data = json.loads(data) @@ -78,9 +80,10 @@ def readfile(filename: str | os.PathLike[str], silent: bool = False, lock: bool def writefile(obj: dict | list, filename: str | os.PathLike[str], mode="w", silent=False, atomic=False): + import copy import tempfile - global locking_available # pylint: disable=global-statement + global locking_available # pylint: disable=global-statement lock_file = None locked = False @@ -90,7 +93,14 @@ def writefile(obj: dict | list, filename: str | os.PathLike[str], mode="w", sile try: t0 = time.time() - data = obj.copy() # Ensure keys/items aren't added/deleted during json.dumps + data = copy.deepcopy(obj) # ensure keys/items aren't added/deleted during json.dumps + for k, v in obj.items() if isinstance(obj, dict) else []: # validate each key-by-key to avoid global exceptions + try: + _tmp = json.dumps(v, indent=2, default=default, allow_nan=False, ensure_ascii=False) + except Exception as err: + if not silent: + log.error(f'Save: file="{filename}" key="{k}" value="{v}" {err}') + del data[k] output = json.dumps(data, indent=2, default=default) except Exception as err: log.error(f'Save failed: file="{filename}" {err}') diff --git a/modules/sd_checkpoint.py b/modules/sd_checkpoint.py index 2f90f0d8c..1b961e5f9 100644 --- a/modules/sd_checkpoint.py +++ b/modules/sd_checkpoint.py @@ -383,7 +383,8 @@ def read_metadata_from_safetensors(filename: str): return res json_data = json_start + file.read(metadata_len-2) json_obj = json.loads(json_data) - for k, v in json_obj.get("__metadata__", {}).items(): + json_dct = json_obj.get("__metadata__", {}) + for k, v in json_dct.items(): if k == 'modelspec.thumbnail' and v.startswith("data:"): extract_thumbnail(filename, v) if v.startswith("data:"): @@ -391,7 +392,12 @@ def read_metadata_from_safetensors(filename: str): if k == 'format' and v == 'pt': continue large = True if len(v) > 2048 else False - if large and k in ['ss_datasets', 'workflow', 'prompt', 'ss_bucket_info', 'sd_metadata_file']: + if large and k in ['ss_datasets', 'prompt', 'ss_bucket_info', 'sd_metadata_file']: + continue + if k in ['workflow']: + res[k] = 'workflow' + continue + if str(v) == 'NaN': continue if v[0:1] == '{': try: @@ -402,7 +408,14 @@ def read_metadata_from_safetensors(filename: str): scrub_dict(v, ['sd_merge_recipe']) except Exception: pass - res[k] = v + if isinstance(v, dict): + try: + _tmp = json.dumps(v, indent=2, default=str, allow_nan=False, ensure_ascii=False) + except Exception: + # log.warning(f'Model metadata: file="{filename}" key="{k}" value="{v}" not serializable') + continue + if v is not None: + res[k] = v except Exception as e: log.error(f'Model metadata: file="{filename}" {e}') from modules import errors @@ -434,6 +447,6 @@ def write_metadata(): if sd_metadata_pending == 0: log.debug(f'Model metadata: file="{sd_metadata_file}" no changes') return - writefile(sd_metadata, sd_metadata_file) - log.info(f'Model metadata saved: file="{sd_metadata_file}" items={sd_metadata_pending} time={sd_metadata_timer:.2f}') + writefile(sd_metadata, sd_metadata_file, silent=True) + log.info(f'Model metadata: file="{sd_metadata_file}" added={sd_metadata_pending} time={sd_metadata_timer:.2f}') sd_metadata_pending = 0 diff --git a/requirements.txt b/requirements.txt index 12ce35b64..3d6e9707d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -27,7 +27,7 @@ safetensors==0.8.0 peft==0.19.1 httpx==0.28.1 requests==2.34.2 -tqdm==4.68.2 +tqdm==4.68.3 accelerate==1.14.0 einops==0.8.2 huggingface_hub==1.21.0 diff --git a/webui.py b/webui.py index 64783f492..39bfa5ed1 100644 --- a/webui.py +++ b/webui.py @@ -92,8 +92,6 @@ def initialize(): def _scan_models(): modules.modelloader.cleanup_models() modules.sd_checkpoint.setup_model() - from modules.sd_checkpoint import write_metadata - write_metadata() def _scan_lora(): from modules.lora import lora_load lora_load.list_available_networks() @@ -109,6 +107,9 @@ def initialize(): future.result() except Exception as e: log.error(f'Scan error: {name} {e}') + from modules.sd_checkpoint import write_metadata + write_metadata() + timer.startup.record("scans") shared.prompt_styles.reload()