import os import safetensors.torch import torch import gc import sys sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) import folder_paths class DiffusersUtils: _model_cache = {'clip': None, 'unet': None, 'vae': None} _current_model_hashes = {'clip': None, 'unet': None, 'vae': None} @staticmethod def clear_memory(): gc.collect() torch.cuda.empty_cache() print("Cleared CUDA Memory and ran garbage collection") @classmethod def clear_model_cache(cls): cls._model_cache = {'clip': None, 'unet': None, 'vae': None} cls._current_model_hashes = {'clip': None, 'unet': None, 'vae': None} cls.clear_memory() print("Cleared model cache") @classmethod def get_model_hash(cls, model_path): import hashlib with open(model_path, "rb") as f: file_hash = hashlib.md5() chunk = f.read(8192) while chunk: file_hash.update(chunk) chunk = f.read(8192) return file_hash.hexdigest() @classmethod def check_and_clear_cache(cls, model_type, model_path): new_hash = cls.get_model_hash(model_path) if cls._current_model_hashes[model_type] != new_hash: print(f"Detected change in {model_type} model. Clearing cache.") cls.clear_model_cache() cls._current_model_hashes[model_type] = new_hash else: print(f"No change detected in {model_type} model.") @staticmethod def get_base_path(): base_path = folder_paths.get_folder_paths("diffusers") #print(f"Base path: {base_path}") if not base_path: raise FileNotFoundError(f"The base path '{base_path}' does not exist.") return base_path @staticmethod def get_model_directories(): paths = [] base_paths = DiffusersUtils.get_base_path() for base_path in base_paths: if os.path.exists(base_path): for root, _, files in os.walk(base_path, followlinks=True): if "model_index.json" in files: relative_path = os.path.relpath(root, start=base_path) full_path = os.path.join(base_path, relative_path) dir_name = os.path.basename(relative_path) paths.append((dir_name, full_path)) return paths @staticmethod def find_model_files(directory, file_parts=None): if not os.path.exists(directory): raise FileNotFoundError(f"The directory '{directory}' does not exist.") files = [os.path.join(directory, f) for f in os.listdir(directory) if f.endswith((".safetensors", ".bin", "index.json"))] if not files: raise FileNotFoundError(f"No .safetensors or .bin or index.json file found in {directory}") if file_parts: filtered_files = [file for file in files if any(part in file for part in file_parts)] else: filtered_files = files print("Filtered files:", filtered_files) return sorted(filtered_files) @staticmethod def find_model_file(directory): files = DiffusersUtils.find_model_files(directory) if files: return files[0] raise FileNotFoundError(f"No .safetensors or .bin pr index.json file found in {directory}") @staticmethod def load_safetensor_paths(file_paths): print("Running Loading Safetensors") combined_tensors = {} for file_path in file_paths: print(f"Loading file: {file_path}") try: tensor_dict = safetensors.torch.load_file(file_path) for key, value in tensor_dict.items(): print(f"Processing key: {key}") if key in combined_tensors: try: combined_tensors[key] = torch.cat((combined_tensors[key], value), dim=0) except RuntimeError as e: print(f"Error concatenating tensors for key {key}: {e}") print(f"Shapes: combined {combined_tensors[key].shape}, new {value.shape}") # Instead of raising, we'll skip this tensor and continue print(f"Skipping problematic tensor: {key}") else: combined_tensors[key] = value print(f"Finished processing file: {file_path}") # Free up memory del tensor_dict torch.cuda.empty_cache() except Exception as e: print(f"Error processing file {file_path}: {e}") # Continue to the next file instead of stopping continue print("Combining Safetensors completed") return combined_tensors #This function is not used. Will be removed in the subsequent release @staticmethod def combine_safetensor_files(directory, base_path, num_parts): print(f"Running combine_safetensor_files for directory: {directory}") part_files = DiffusersUtils.find_model_files(directory, num_parts=num_parts) print("Path to part_files:", part_files) try: combined_tensors = DiffusersUtils.load_safetensor_paths(part_files) print("Combined tensors keys:", list(combined_tensors.keys())) if "text_encoder" in directory: combined_file_path = os.path.join(directory, "combined_text_encoder.safetensors") elif "transformer" in directory: combined_file_path = os.path.join(directory, "combined_transformer.safetensors") else: raise ValueError(f"Unsupported directory for combining files: {directory}") print(f"Saving combined file to: {combined_file_path}") safetensors.torch.save_file(combined_tensors, combined_file_path) print("Combined file saved successfully") return combined_file_path except Exception as e: print(f"Error in combine_safetensor_files: {e}") # If combining fails, return the path to the first file as a fallback return part_files[0] @staticmethod def get_unique_display_names(model_directories): unique_names = {} for dir_name, full_path in model_directories: if dir_name in unique_names: unique_names[dir_name].append(full_path) else: unique_names[dir_name] = [full_path] display_names = [] for dir_name, paths in unique_names.items(): if len(paths) == 1: display_names.append(dir_name) else: for i, path in enumerate(paths): display_names.append(f"{dir_name} ({i+1})") return display_names, unique_names @staticmethod def get_full_path(dir_name): model_directories = DiffusersUtils.get_model_directories() for name, path in model_directories: if name == dir_name: return path raise ValueError(f"Directory not found: {dir_name}")