├── cog_model_helpers ├── __init__.py ├── seed.py └── optimise_images.py ├── scripts ├── start.sh ├── get_weights_completion.sh ├── reset.py ├── prepare_template.py ├── get_weights.py ├── install_custom_nodes.py └── upgrade_custom_nodes.py ├── custom_node_configs └── comfy.settings.json ├── custom_nodes.json ├── README.md ├── .gitmodules ├── .gitignore ├── config.py ├── custom_node_helpers └── __init__.py ├── reset.json ├── custom_node_helper.py ├── comfyui_enums.py ├── node.py ├── workflow_api.json ├── .dockerignore ├── cog.yaml ├── LICENSE ├── weights_downloader.py ├── MAKING_A_MODEL_GUIDE.md ├── weights_manifest.py ├── predict.py ├── comfyui.py └── weights.json /cog_model_helpers/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /scripts/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd ComfyUI 3 | python main.py --listen 0.0.0.0 --gpu-only 4 | -------------------------------------------------------------------------------- /custom_node_configs/comfy.settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "pysssss.ShowImageOnMenu": false, 3 | "Comfy.DevMode": true 4 | } -------------------------------------------------------------------------------- /custom_nodes.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "repo": "https://github.com/fofr/ComfyUI-AdvancedLivePortrait", 4 | "commit": "a8ae7ed" 5 | } 6 | ] 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Your repo 2 | 3 | [![Try a demo on Replicate](https://replicate.com/fofr/expression-editor/badge)](https://replicate.com/fofr/expression-editor) 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "ComfyUI"] 2 | path = ComfyUI 3 | url = https://github.com/comfyanonymous/ComfyUI.git 4 | commit = 3a0eeee32053f6c45c5ad4b3975f4ee229b01fbb 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | *.png 3 | *.jpg 4 | *.gif 5 | *.mp4 6 | *.zip 7 | *.ckpt 8 | *.safetensors 9 | *.pth 10 | *.bin 11 | *.torchscript 12 | *.webp 13 | *.engine 14 | weights.txt 15 | manifest.txt 16 | updated_weights.json 17 | downloaded_user_models/ 18 | .cog 19 | -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | config = { 2 | "WEIGHTS_BASE_URL": "https://weights.replicate.delivery/default/comfy-ui", 3 | "REMOTE_WEIGHTS_MANIFEST_URL": "https://raw.githubusercontent.com/fofr/cog-comfyui/main/weights.json", 4 | "MODELS_PATH": "ComfyUI/models", 5 | "USER_WEIGHTS_PATH": "downloaded_user_models", 6 | "USER_WEIGHTS_MANIFEST_PATH": "downloaded_user_models/weights.json", 7 | } 8 | -------------------------------------------------------------------------------- /custom_node_helpers/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import importlib 4 | 5 | current_dir = os.path.dirname(os.path.abspath(__file__)) 6 | for file in os.listdir(current_dir): 7 | if file.endswith(".py") and not file.startswith("__"): 8 | module_name = file[:-3] 9 | module = importlib.import_module(f".{module_name}", package=__name__) 10 | class_name = module_name 11 | setattr(sys.modules[__name__], class_name, getattr(module, class_name)) 12 | -------------------------------------------------------------------------------- /reset.json: -------------------------------------------------------------------------------- 1 | { 2 | "3": { 3 | "inputs": { 4 | "width": 512, 5 | "height": 512, 6 | "batch_size": 1, 7 | "color": 0 8 | }, 9 | "class_type": "EmptyImage", 10 | "_meta": { 11 | "title": "EmptyImage" 12 | } 13 | }, 14 | "4": { 15 | "inputs": { 16 | "images": [ 17 | "3", 18 | 0 19 | ] 20 | }, 21 | "class_type": "PreviewImage", 22 | "_meta": { 23 | "title": "Preview Image" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /cog_model_helpers/seed.py: -------------------------------------------------------------------------------- 1 | from cog import Input 2 | import random 3 | 4 | 5 | def predict_seed() -> int: 6 | return Input( 7 | description="Set a seed for reproducibility. Random by default.", 8 | default=None, 9 | ) 10 | 11 | 12 | def generate(seed: int) -> int: 13 | if seed is None or seed < 0: 14 | seed = random.randint(0, 2**32 - 1) 15 | print(f"Random seed set to: {seed}") 16 | else: 17 | print(f"Seed set to: {seed}") 18 | return seed 19 | -------------------------------------------------------------------------------- /scripts/get_weights_completion.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # source ./scripts/get_weights_completion.sh 3 | 4 | _get_weights_completion() { 5 | local cur="${COMP_WORDS[COMP_CWORD]}" 6 | local weights_file="weights.json" 7 | 8 | if [[ -f $weights_file ]]; then 9 | local options=$(jq -r 'paths(scalars) as $p | getpath($p) | select(. != null)' $weights_file | sort -u | tr '\n' ' ') 10 | COMPREPLY=($(compgen -W "$options" -- "$cur")) 11 | fi 12 | } 13 | 14 | complete -F _get_weights_completion ./scripts/get_weights.py 15 | -------------------------------------------------------------------------------- /custom_node_helper.py: -------------------------------------------------------------------------------- 1 | class CustomNodeHelper: 2 | # This class provides helper methods for managing node weights and checking unsupported nodes. 3 | 4 | @staticmethod 5 | def prepare(**kwargs): 6 | # Placeholder method to prepare a custom node before ComfyUI starts 7 | pass 8 | 9 | @staticmethod 10 | def weights_map(base_url): 11 | # Placeholder method for mapping weights based on a base URL. 12 | return {} 13 | 14 | @staticmethod 15 | def add_weights(weights_to_download, node): 16 | # Placeholder method to add weights to download list based on node specifications. 17 | pass 18 | 19 | @staticmethod 20 | def check_for_unsupported_nodes(node): 21 | # Placeholder method to check if a node is not supported. 22 | pass 23 | -------------------------------------------------------------------------------- /comfyui_enums.py: -------------------------------------------------------------------------------- 1 | SAMPLERS = [ 2 | "euler", 3 | "euler_cfg_pp", 4 | "euler_ancestral", 5 | "euler_ancestral_cfg_pp", 6 | "heun", 7 | "heunpp2", 8 | "dpm_2", 9 | "dpm_2_ancestral", 10 | "lms", 11 | "dpm_fast", 12 | "dpm_adaptive", 13 | "dpmpp_2s_ancestral", 14 | "dpmpp_2s_ancestral_RF", 15 | "dpmpp_sde", 16 | "dpmpp_sde_gpu", 17 | "dpmpp_2m", 18 | "dpmpp_2m_sde", 19 | "dpmpp_2m_sde_gpu", 20 | "dpmpp_3m_sde", 21 | "dpmpp_3m_sde_gpu", 22 | "ddpm", 23 | "lcm", 24 | "ddim", 25 | "uni_pc", 26 | "uni_pc_bh2", 27 | "ipndm", 28 | "ipndm_v", 29 | "deis", 30 | ] 31 | 32 | SCHEDULERS = [ 33 | "normal", 34 | "karras", 35 | "exponential", 36 | "sgm_uniform", 37 | "simple", 38 | "ddim_uniform", 39 | ] 40 | -------------------------------------------------------------------------------- /scripts/reset.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import subprocess 3 | 4 | """ 5 | This script is used to reset the ComfyUI environment. 6 | It deletes the ComfyUI directory before reinstalling ComfyUI and every custom node. 7 | """ 8 | 9 | print("Preparing to reset the ComfyUI environment...") 10 | print( 11 | "This will delete the ComfyUI directory before reinstalling ComfyUI and every custom node." 12 | ) 13 | print("Are you sure you want to continue? (y/n)") 14 | 15 | if input() != "y": 16 | print("Aborting...") 17 | exit(1) 18 | 19 | print("Deleting the ComfyUI directory...") 20 | subprocess.run(["sudo", "rm", "-rf", "ComfyUI"]) 21 | 22 | print("Installing ComfyUI...") 23 | subprocess.run(["git", "submodule", "update", "--init", "--recursive"]) 24 | 25 | print("Installing custom nodes...") 26 | subprocess.run(["./scripts/install_custom_nodes.py"]) 27 | print("Custom nodes installed successfully.") 28 | -------------------------------------------------------------------------------- /node.py: -------------------------------------------------------------------------------- 1 | class Node: 2 | def __init__(self, node): 3 | self.node = node 4 | 5 | def type(self): 6 | return self.node["class_type"] 7 | 8 | def is_type(self, type): 9 | return "class_type" in self.node and self.node["class_type"] == type 10 | 11 | def is_type_in(self, types): 12 | return "class_type" in self.node and self.node["class_type"] in types 13 | 14 | def has_input(self, key): 15 | return key in self.node["inputs"] 16 | 17 | def input(self, key, default_value=None): 18 | return self.node["inputs"][key] if key in self.node["inputs"] else default_value 19 | 20 | def set_input(self, key, value): 21 | self.node["inputs"][key] = value 22 | 23 | def raise_if_unsupported(self, unsupported_nodes={}): 24 | if self.is_type_in(unsupported_nodes): 25 | raise ValueError(f"{self.type()} node is not supported: {unsupported_nodes[self.type()]}") 26 | -------------------------------------------------------------------------------- /workflow_api.json: -------------------------------------------------------------------------------- 1 | { 2 | "14": { 3 | "inputs": { 4 | "rotate_pitch": 0, 5 | "rotate_yaw": 0.30000000000000004, 6 | "rotate_roll": 1.6, 7 | "blink": 5, 8 | "eyebrow": 1.9000000000000001, 9 | "wink": 13.100000000000001, 10 | "pupil_x": 15, 11 | "pupil_y": 6.4, 12 | "aaa": 120, 13 | "eee": 15, 14 | "woo": 15, 15 | "smile": -0.3, 16 | "src_ratio": 0.76, 17 | "sample_ratio": 0.67, 18 | "sample_parts": "OnlyExpression", 19 | "crop_factor": 1.7000000000000002, 20 | "src_image": [ 21 | "15", 22 | 0 23 | ] 24 | }, 25 | "class_type": "ExpressionEditor", 26 | "_meta": { 27 | "title": "Expression Editor (PHM)" 28 | } 29 | }, 30 | "15": { 31 | "inputs": { 32 | "image": "input.png", 33 | "upload": "image" 34 | }, 35 | "class_type": "LoadImage", 36 | "_meta": { 37 | "title": "Load Image" 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | **/__pycache__ 2 | **/.git 3 | **/.github 4 | **/.ci 5 | 6 | # Outputs 7 | *.jpg 8 | *.png 9 | *.gif 10 | *.mp4 11 | *.zip 12 | *.tar 13 | *.webp 14 | *.webm 15 | 16 | # Models 17 | *.ckpt 18 | *.safetensors 19 | *.pth 20 | *.bin 21 | *.onnx 22 | *.torchscript 23 | *.engine 24 | # .pt files are used by efficiency-nodes-comfyui 25 | 26 | # Files 27 | scripts/* 28 | test/* 29 | updated_weights.json 30 | downloaded_user_models/ 31 | 32 | # Extension files 33 | *.ipynb 34 | *.bat 35 | 36 | # ComfyUI 37 | ComfyUI/venv 38 | ComfyUI/temp 39 | ComfyUI/user 40 | ComfyUI/models 41 | ComfyUI/custom_nodes/comfyUI_controlnet_aux/ckpts 42 | ComfyUI/custom_nodes/ComfyUI-AnimateDiff-Evolved/models 43 | ComfyUI/custom_nodes/ComfyUI-AnimateDiff-Evolved/motion_lora 44 | ComfyUI/custom_nodes/efficiency-nodes-comfyui/images 45 | 46 | # ComfyUI bits we just don’t need 47 | ComfyUI/tests 48 | ComfyUI/tests-ui 49 | ComfyUI/tests-unit 50 | ComfyUI/notebooks 51 | ComfyUI/script_examples 52 | ComfyUI/comfyui_screenshot.png 53 | -------------------------------------------------------------------------------- /cog.yaml: -------------------------------------------------------------------------------- 1 | build: 2 | gpu: true 3 | cuda: "12.1" 4 | system_packages: 5 | - ffmpeg 6 | python_version: "3.10.6" 7 | python_packages: 8 | - torch 9 | - torchvision 10 | - torchaudio 11 | - torchsde 12 | - einops 13 | - transformers>=4.28.1 14 | - tokenizers>=0.13.3 15 | - sentencepiece 16 | - safetensors>=0.3.0 17 | - aiohttp 18 | - accelerate 19 | - pyyaml 20 | - Pillow 21 | - scipy 22 | - tqdm 23 | - psutil 24 | - spandrel 25 | - soundfile 26 | - kornia>=0.7.1 27 | - websocket-client==1.6.3 28 | 29 | # fix for pydantic issues in cog 30 | # https://github.com/replicate/cog/issues/1623 31 | - albumentations==1.4.3 32 | 33 | - numpy>=1.26.4 34 | - opencv-python-headless 35 | - imageio-ffmpeg>=0.5.1 36 | - lmdb>=1.4.1 37 | - rich>=13.7.1 38 | - ultralytics 39 | - tyro==0.8.5 40 | - dill 41 | run: 42 | - curl -o /usr/local/bin/pget -L "https://github.com/replicate/pget/releases/download/v0.8.1/pget_linux_x86_64" && chmod +x /usr/local/bin/pget 43 | predict: "predict.py:Predictor" 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 fofr 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /cog_model_helpers/optimise_images.py: -------------------------------------------------------------------------------- 1 | from cog import Input 2 | from PIL import Image 3 | 4 | IMAGE_FILE_EXTENSIONS = [".jpg", ".jpeg", ".png"] 5 | FORMAT_CHOICES = ["webp", "jpg", "png"] 6 | DEFAULT_FORMAT = "webp" 7 | DEFAULT_QUALITY = 95 8 | 9 | 10 | def predict_output_format() -> str: 11 | return Input( 12 | description="Format of the output images", 13 | choices=FORMAT_CHOICES, 14 | default=DEFAULT_FORMAT, 15 | ) 16 | 17 | 18 | def predict_output_quality() -> int: 19 | return Input( 20 | description="Quality of the output images, from 0 to 100. 100 is best quality, 0 is lowest quality.", 21 | default=DEFAULT_QUALITY, 22 | ge=0, 23 | le=100, 24 | ) 25 | 26 | 27 | def should_optimise_images(output_format: str, output_quality: int): 28 | return output_quality < 100 or output_format in [ 29 | "webp", 30 | "jpg", 31 | ] 32 | 33 | 34 | def optimise_image_files( 35 | output_format: str = DEFAULT_FORMAT, output_quality: int = DEFAULT_QUALITY, files=[] 36 | ): 37 | if should_optimise_images(output_format, output_quality): 38 | optimised_files = [] 39 | for file in files: 40 | if file.is_file() and file.suffix in IMAGE_FILE_EXTENSIONS: 41 | image = Image.open(file) 42 | optimised_file_path = file.with_suffix(f".{output_format}") 43 | image.save( 44 | optimised_file_path, 45 | quality=output_quality, 46 | optimize=True, 47 | ) 48 | optimised_files.append(optimised_file_path) 49 | else: 50 | optimised_files.append(file) 51 | 52 | return optimised_files 53 | else: 54 | return files 55 | -------------------------------------------------------------------------------- /scripts/prepare_template.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import os 3 | import shutil 4 | 5 | FILES_TO_DELETE = [ 6 | "examples", 7 | "test", 8 | "CHANGELOG.md", 9 | "supported_weights.md", 10 | "weights_licenses.md", 11 | "scripts/push_comfyui_manager_weights.py", 12 | "scripts/push_weights_from_hf.py", 13 | "scripts/push_weights.py", 14 | "scripts/sort_weights.py", 15 | "train.py", 16 | ] 17 | 18 | 19 | def prepare_template(): 20 | """ 21 | This script is used to prepare the template for a new model. 22 | It deletes unnecessary files and directories. 23 | It also overwrites the README.md with a blank file and header. 24 | Finally, it replaces predict.py with example_predict.py. 25 | """ 26 | print("Preparing to clean up this template for a new model") 27 | print( 28 | "This will clear the README and delete the following files and directories:", 29 | "\n".join(FILES_TO_DELETE), 30 | ) 31 | print("Are you sure you want to continue? (y/n)") 32 | 33 | if input() != "y": 34 | print("Aborting...") 35 | exit(1) 36 | 37 | print("Deleting unnecessary files and directories...") 38 | for file in FILES_TO_DELETE: 39 | if os.path.exists(file): 40 | if os.path.isdir(file): 41 | shutil.rmtree(file) 42 | else: 43 | os.remove(file) 44 | 45 | # Overwrite the README.md with a blank file and header "# Your repo" 46 | print("Overwriting README.md with a blank file and header") 47 | with open("README.md", "w") as f: 48 | f.write("# Your repo\n") 49 | 50 | print("Replacing predict.py with example_predict.py") 51 | shutil.move("example_predict.py", "predict.py") 52 | 53 | print("Removing train script from cog.yaml") 54 | with open("cog.yaml", "r") as f: 55 | lines = f.readlines() 56 | with open("cog.yaml", "w") as f: 57 | for line in lines: 58 | if line.strip() != 'train: "train.py:train"': 59 | f.write(line) 60 | 61 | 62 | prepare_template() 63 | -------------------------------------------------------------------------------- /scripts/get_weights.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | This script is used to download weight files specified in various input formats. 5 | It supports reading weight file names from plain text files, extracting them from JSON workflows, 6 | or directly from command-line arguments. The script utilizes the WeightsDownloader class 7 | to handle the actual downloading of the weight files. 8 | """ 9 | 10 | import sys 11 | import os 12 | import json 13 | 14 | sys.path.append(os.path.join(os.path.dirname(__file__), "..")) 15 | from weights_downloader import WeightsDownloader 16 | 17 | PREDEFINED_WEIGHT_SETS = { 18 | "flux": [ 19 | "flux1-dev.safetensors", 20 | "ae.safetensors", 21 | "clip_l.safetensors", 22 | "t5xxl_fp8_e4m3fn.safetensors", 23 | ] 24 | } 25 | 26 | 27 | def download_weight_files(weight_files): 28 | wd = WeightsDownloader() 29 | for weight_file in weight_files: 30 | try: 31 | wd.download_weights(weight_file) 32 | except Exception as e: 33 | print(f"Failed to download {weight_file}: {str(e)}") 34 | continue 35 | 36 | 37 | def extract_weights_from_workflow(workflow_path): 38 | with open(workflow_path, "r") as f: 39 | workflow = json.load(f) 40 | weights_to_download = [] 41 | for node in workflow.values(): 42 | if "inputs" in node: 43 | for input in node["inputs"].values(): 44 | if isinstance(input, str) and input.endswith( 45 | tuple(WeightsDownloader.supported_filetypes) 46 | ): 47 | weights_to_download.append(input) 48 | return list(set(weights_to_download)) 49 | 50 | 51 | def main(filenames): 52 | weight_files = [] 53 | for filename in filenames: 54 | if filename in PREDEFINED_WEIGHT_SETS: 55 | weight_files.extend(PREDEFINED_WEIGHT_SETS[filename]) 56 | elif filename.endswith(".txt"): 57 | with open(filename, "r") as f: 58 | weight_files.extend(f.read().splitlines()) 59 | elif filename.endswith(".json"): 60 | weight_files.extend(extract_weights_from_workflow(filename)) 61 | else: 62 | weight_files.append(filename) 63 | download_weight_files(weight_files) 64 | 65 | 66 | if __name__ == "__main__": 67 | if len(sys.argv) < 2: 68 | print( 69 | "Usage: python get_weights.py [ ...] or python get_weights.py or python get_weights.py " 70 | ) 71 | sys.exit(1) 72 | filenames = sys.argv[1:] 73 | main(filenames) 74 | -------------------------------------------------------------------------------- /weights_downloader.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import time 3 | import os 4 | from weights_manifest import WeightsManifest 5 | 6 | 7 | class WeightsDownloader: 8 | supported_filetypes = [ 9 | ".ckpt", 10 | ".safetensors", 11 | ".pt", 12 | ".pth", 13 | ".bin", 14 | ".onnx", 15 | ".torchscript", 16 | ".engine", 17 | ".patch" 18 | ] 19 | 20 | def __init__(self): 21 | self.weights_manifest = WeightsManifest() 22 | self.weights_map = self.weights_manifest.weights_map 23 | 24 | def get_weights_by_type(self, type): 25 | return self.weights_manifest.get_weights_by_type(type) 26 | 27 | def download_weights(self, weight_str): 28 | if weight_str in self.weights_map: 29 | if self.weights_manifest.is_non_commercial_only(weight_str): 30 | print( 31 | f"⚠️ {weight_str} is for non-commercial use only. Unless you have obtained a commercial license.\nDetails: https://github.com/fofr/cog-comfyui/blob/main/weights_licenses.md" 32 | ) 33 | 34 | if isinstance(self.weights_map[weight_str], list): 35 | for weight in self.weights_map[weight_str]: 36 | self.download_if_not_exists( 37 | weight_str, weight["url"], weight["dest"] 38 | ) 39 | else: 40 | self.download_if_not_exists( 41 | weight_str, 42 | self.weights_map[weight_str]["url"], 43 | self.weights_map[weight_str]["dest"], 44 | ) 45 | else: 46 | raise ValueError( 47 | f"{weight_str} unavailable. View the list of available weights: https://github.com/fofr/cog-comfyui/blob/main/supported_weights.md" 48 | ) 49 | 50 | def check_if_file_exists(self, weight_str, dest): 51 | if dest.endswith(weight_str): 52 | path_string = dest 53 | else: 54 | path_string = os.path.join(dest, weight_str) 55 | return os.path.exists(path_string) 56 | 57 | def download_if_not_exists(self, weight_str, url, dest): 58 | if self.check_if_file_exists(weight_str, dest): 59 | print(f"✅ {weight_str} exists in {dest}") 60 | return 61 | WeightsDownloader.download(weight_str, url, dest) 62 | 63 | @staticmethod 64 | def download(weight_str, url, dest): 65 | if "/" in weight_str: 66 | subfolder = weight_str.rsplit("/", 1)[0] 67 | dest = os.path.join(dest, subfolder) 68 | os.makedirs(dest, exist_ok=True) 69 | 70 | print(f"⏳ Downloading {weight_str} to {dest}") 71 | start = time.time() 72 | subprocess.check_call( 73 | ["pget", "--log-level", "warn", "-xf", url, dest], close_fds=False 74 | ) 75 | elapsed_time = time.time() - start 76 | try: 77 | file_size_bytes = os.path.getsize( 78 | os.path.join(dest, os.path.basename(weight_str)) 79 | ) 80 | file_size_megabytes = file_size_bytes / (1024 * 1024) 81 | print( 82 | f"✅ {weight_str} downloaded to {dest} in {elapsed_time:.2f}s, size: {file_size_megabytes:.2f}MB" 83 | ) 84 | except FileNotFoundError: 85 | print(f"✅ {weight_str} downloaded to {dest} in {elapsed_time:.2f}s") 86 | -------------------------------------------------------------------------------- /scripts/install_custom_nodes.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import json 4 | import os 5 | import subprocess 6 | 7 | """ 8 | This script is used to clone specific versions of repositories. 9 | It reads a JSON file containing repositories and their commit hashes, clones them into a specific directory, 10 | and then checks out to the specified commit. 11 | """ 12 | 13 | json_file = "custom_nodes.json" 14 | comfy_dir = "ComfyUI" 15 | custom_nodes_dir = f"{comfy_dir}/custom_nodes/" 16 | 17 | with open(json_file, "r") as file: 18 | repos = json.load(file) 19 | 20 | # Loop over each repository in the list 21 | for repo in repos: 22 | repo_url = repo["repo"] 23 | commit_hash = repo["commit"] 24 | repo_name = os.path.basename(repo_url.replace(".git", "")) 25 | 26 | # Check if the repository directory already exists 27 | repo_path = os.path.join(custom_nodes_dir, repo_name) 28 | if not os.path.isdir(repo_path): 29 | # Clone the repository into the destination directory 30 | print( 31 | f"Cloning {repo_url} into {repo_path} and checking out to commit {commit_hash}" 32 | ) 33 | subprocess.run(["git", "clone", "--recursive", repo_url, repo_path]) 34 | 35 | # Store the current directory and change to the repository's directory 36 | current_dir = os.getcwd() 37 | os.chdir(repo_path) 38 | subprocess.run(["git", "checkout", commit_hash]) 39 | subprocess.run(["git", "submodule", "update", "--init", "--recursive"]) 40 | 41 | # Change back to the original directory after operations 42 | os.chdir(current_dir) 43 | else: 44 | current_dir = os.getcwd() 45 | os.chdir(repo_path) 46 | 47 | current_commit = ( 48 | subprocess.check_output(["git", "rev-parse", "HEAD"]).decode().strip() 49 | ) 50 | print(f"Custom node installed for {repo_name} at {current_commit[:7]}") 51 | 52 | if current_commit[:7] != commit_hash[:7]: 53 | response = input( 54 | f"Do you want to update {repo_name}? Current ({current_commit[:7]}) is different from ({commit_hash[:7]}) (y/n): " 55 | ) 56 | if response.lower() == "y": 57 | print(f"Checking out to commit {commit_hash}") 58 | subprocess.run(["git", "fetch"]) 59 | subprocess.run(["git", "checkout", commit_hash]) 60 | subprocess.run(["git", "submodule", "update", "--init", "--recursive"]) 61 | else: 62 | print("Skipping checkout, keeping current commit") 63 | 64 | os.chdir(current_dir) 65 | 66 | # Copy custom node config files to the correct directory 67 | config_files = { 68 | "was_suite_config": { 69 | "src": "custom_node_configs/was_suite_config.json", 70 | "dest": os.path.join(custom_nodes_dir, "was-node-suite-comfyui/"), 71 | }, 72 | "rgthree_config": { 73 | "src": "custom_node_configs/rgthree_config.json", 74 | "dest": os.path.join(custom_nodes_dir, "rgthree-comfy/"), 75 | }, 76 | "comfy_settings": { 77 | "src": "custom_node_configs/comfy.settings.json", 78 | "dest": os.path.join(comfy_dir, "user", "default"), 79 | }, 80 | } 81 | 82 | if "comfy_settings" in config_files: 83 | paths = config_files["comfy_settings"] 84 | if not os.path.exists(paths["dest"]): 85 | os.makedirs(paths["dest"]) 86 | 87 | for config_file, paths in config_files.items(): 88 | if ( 89 | os.path.isfile(paths["src"]) 90 | and os.path.isdir(paths["dest"]) 91 | and not os.path.exists( 92 | os.path.join(paths["dest"], os.path.basename(paths["src"])) 93 | ) 94 | ): 95 | print(f"Copying {config_file} to {paths['dest']}") 96 | subprocess.run(["cp", paths["src"], paths["dest"]]) 97 | -------------------------------------------------------------------------------- /scripts/upgrade_custom_nodes.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import json 4 | import os 5 | import subprocess 6 | from datetime import datetime 7 | 8 | """ 9 | This script checks for updates to custom node repositories. 10 | It fetches the latest commit on the main branch for each repo, 11 | prompts the user if they want to update, and updates the JSON file and repo if confirmed. 12 | """ 13 | 14 | json_file = "custom_nodes.json" 15 | comfy_dir = "ComfyUI" 16 | custom_nodes_dir = f"{comfy_dir}/custom_nodes/" 17 | changelog_file = "CHANGELOG.md" 18 | 19 | def get_latest_commit(repo_path): 20 | try: 21 | subprocess.run(["git", "fetch", "origin", "main"], cwd=repo_path, check=True, capture_output=True) 22 | result = subprocess.run(["git", "rev-parse", "origin/main"], cwd=repo_path, check=True, capture_output=True, text=True) 23 | return result.stdout.strip() 24 | except subprocess.CalledProcessError: 25 | print(f"Failed to fetch latest commit for {repo_path}") 26 | return None 27 | 28 | def update_json_file(repos): 29 | with open(json_file, "w") as file: 30 | json.dump(repos, file, indent=2) 31 | file.write("\n") 32 | 33 | 34 | def update_changelog(repo_name, compare_url): 35 | today = datetime.now().strftime("%Y-%m-%d") 36 | update_line = f"- [Updated {repo_name}]({compare_url})\n" 37 | 38 | try: 39 | with open(changelog_file, "r+") as file: 40 | content = file.readlines() 41 | if content[0].strip() != f"## {today}": 42 | content.insert(0, f"\n## {today}\n\n") 43 | 44 | # Find the index of the current day's section 45 | today_index = next((i for i, line in enumerate(content) if line.strip() == f"## {today}"), None) 46 | 47 | if today_index is not None: 48 | # Insert the update line right after the current day's header 49 | content.insert(today_index + 1, update_line) 50 | else: 51 | # If today's section wasn't found (which shouldn't happen), append to the top 52 | content.insert(2, update_line) 53 | 54 | file.seek(0) 55 | file.writelines(content) 56 | except FileNotFoundError: 57 | print(f"Warning: Changelog file '{changelog_file}' not found. Skipping changelog update.") 58 | except IOError as e: 59 | print(f"Error updating changelog: {e}") 60 | 61 | with open(json_file, "r") as file: 62 | repos = json.load(file) 63 | 64 | # Loop over each repository in the list 65 | for repo in repos: 66 | repo_url = repo["repo"] 67 | current_commit = repo["commit"] 68 | repo_name = os.path.basename(repo_url.replace(".git", "")) 69 | 70 | print(f"\nChecking {repo_name}...") 71 | 72 | # Check if the repository directory exists 73 | repo_path = os.path.join(custom_nodes_dir, repo_name) 74 | if not os.path.isdir(repo_path): 75 | print(f"Repository {repo_name} not found. Skipping.") 76 | continue 77 | 78 | # Get the latest commit on the main branch 79 | latest_commit = get_latest_commit(repo_path) 80 | 81 | if latest_commit is None: 82 | print(f"Failed to fetch latest commit for {repo_name}. Skipping.") 83 | continue 84 | 85 | if latest_commit[:7] != current_commit[:7]: 86 | print(f"Update available for {repo_name}") 87 | print(f"Current commit: {current_commit[:7]}") 88 | print(f"Latest commit: {latest_commit[:7]}") 89 | 90 | # Generate GitHub comparison URL 91 | compare_url = f"{repo_url}/compare/{current_commit[:7]}...{latest_commit[:7]}" 92 | print(f"Comparison URL: {compare_url}") 93 | 94 | response = input("Do you want to update? (y/n): ") 95 | if response.lower() == 'y': 96 | print(f"Updating {repo_name}...") 97 | subprocess.run(["git", "checkout", latest_commit], cwd=repo_path, check=True) 98 | subprocess.run(["git", "submodule", "update", "--init", "--recursive"], cwd=repo_path, check=True) 99 | 100 | repo["commit"] = latest_commit[:7] 101 | update_json_file(repos) 102 | update_changelog(repo_name, compare_url) 103 | 104 | print(f"Updated {repo_name} to {latest_commit[:7]}") 105 | else: 106 | print(f"Skipping update for {repo_name}") 107 | else: 108 | print(f"{repo_name} is up to date") 109 | 110 | print("\nFinished checking for updates. custom_nodes.json and CHANGELOG.md have been updated if necessary.") 111 | -------------------------------------------------------------------------------- /MAKING_A_MODEL_GUIDE.md: -------------------------------------------------------------------------------- 1 | # Adapting this template for your model 2 | 3 | This guide will help you adapt the `cog-comfyui` template repository for your own model. 4 | 5 | If you haven’t used `cog` before or pushed a Replicate model, these are good starting guides: 6 | 7 | - https://cog.run/getting-started-own-model 8 | - https://replicate.com/docs/guides/push-a-model 9 | 10 | 11 | ## Create a new repo from the template 12 | 13 | Use https://github.com/fofr/cog-comfyui as a template to create a new repository 14 | 15 | ## Prepare the template 16 | 17 | After you have git cloned your new repository locally, including submodules, you should run the `prepare_template.py` script. 18 | 19 | This will: 20 | 21 | - delete unnecessary files and directories. 22 | - overwrite the `README.md` with a blank file and header. 23 | - replace `predict.py` with `example_predict.py` 24 | 25 | Run this script: 26 | 27 | ```sh 28 | python scripts/prepare_template.py 29 | ``` 30 | 31 | Check what has been deleted and replaced before committing: 32 | 33 | ```sh 34 | git status 35 | git diff 36 | ``` 37 | 38 | ## Add your workflow 39 | 40 | You should save the API version of your workflow as `workflow_api.json`. 41 | 42 | It also helps to keep a copy of the UI version too, as `workflow_ui.json`. 43 | 44 | ## Update the inputs to your model 45 | 46 | `predict.py` is the entrypoint to your model. You can read about `predict.py` and the inputs you can use in the [Cog documentation](https://cog.run/python/#predictorpredictkwargs). 47 | 48 | You'll end up with something like this: 49 | 50 | ```python 51 | def predict( 52 | self, 53 | prompt: str = Input( 54 | default="", 55 | ), 56 | negative_prompt: str = Input( 57 | description="Things you do not want to see in your image", 58 | default="", 59 | ), 60 | image: Path = Input( 61 | description="An input image", 62 | default=None, 63 | ), 64 | ... 65 | ) -> List[Path]: 66 | """Run a single prediction on the model""" 67 | ... 68 | ``` 69 | 70 | To make sure these inputs carry over to the workflow, you'll need to update the JSON object with new values. `example_predict.py` includes an example of this: 71 | 72 | Within the predict method: 73 | 74 | ```python 75 | self.update_workflow( 76 | workflow, 77 | prompt=prompt, 78 | negative_prompt=negative_prompt, 79 | seed=seed, 80 | ) 81 | ``` 82 | 83 | And in the `update_workflow` method (in the Predictor class): 84 | 85 | ```python 86 | def update_workflow(self, workflow, **kwargs): 87 | # The node 88 | positive_prompt = workflow["6"]["inputs"] 89 | 90 | # Updating one of the nodes inputs 91 | positive_prompt["text"] = kwargs["prompt"] 92 | 93 | negative_prompt = workflow["7"]["inputs"] 94 | negative_prompt["text"] = f"nsfw, {kwargs['negative_prompt']}" 95 | 96 | sampler = workflow["3"]["inputs"] 97 | sampler["seed"] = kwargs["seed"] 98 | ``` 99 | 100 | ## Remove any custom nodes you do not need 101 | 102 | To remove a custom node you should: 103 | 104 | - remove its corresponding entry from the `custom_nodes.json` file 105 | - optional: delete any corresponding helpers in `custom_node_helpers` 106 | - optional: delete any configs from `custom_nodes_configs` 107 | - optional: delete dependencies from `cog.yaml` 108 | 109 | If you've already installed the nodes, make sure to also remove it from the `ComfyUI/custom_nodes` directory. 110 | 111 | ## Add your own custom nodes 112 | 113 | The simplest way to add new nodes is to: 114 | 115 | - add a new entry to the `custom_nodes.json` file, with the repo URL and the commit hash you want to use (usually the latest) 116 | - add any dependencies from the custom node’s `requirements.txt` to the `cog.yaml` file (if they are not already there) 117 | - rerun `scripts/install_custom_nodes.py` to install the new nodes 118 | 119 | Some nodes will try to download weights on demand. You might want to avoid doing this from your Replicate model. The easiest fix is to make sure the downloaded weights are also pushed with your container. If you choose to do this, make sure to also update `.dockerignore`, which by default excludes weight from the container. 120 | 121 | ## Running the model locally 122 | 123 | You can run the model with defaults via cog: 124 | 125 | ```sh 126 | cog predict 127 | ``` 128 | 129 | Or if you want to pass inputs: 130 | 131 | ```sh 132 | cog predict -i prompt="something" -i image=@/path/to/image.jpg 133 | ``` 134 | 135 | ## Deploying your model to Replicate 136 | 137 | Create a new model from https://replicate.com/create 138 | 139 | Push your cog container to Replicate: 140 | 141 | ```sh 142 | cog push r8.im// 143 | ``` 144 | -------------------------------------------------------------------------------- /weights_manifest.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import time 3 | import os 4 | import json 5 | import custom_node_helpers as helpers 6 | from config import config 7 | 8 | USER_WEIGHTS_MANIFEST_PATH = config["USER_WEIGHTS_MANIFEST_PATH"] 9 | REMOTE_WEIGHTS_MANIFEST_URL = config["REMOTE_WEIGHTS_MANIFEST_URL"] 10 | REMOTE_WEIGHTS_MANIFEST_PATH = "updated_weights.json" 11 | WEIGHTS_MANIFEST_PATH = "weights.json" 12 | BASE_URL = config["WEIGHTS_BASE_URL"] 13 | MODELS_PATH = config["MODELS_PATH"] 14 | 15 | 16 | class WeightsManifest: 17 | @staticmethod 18 | def base_url(): 19 | return BASE_URL 20 | 21 | def __init__(self): 22 | self.download_latest_weights_manifest = ( 23 | os.getenv("DOWNLOAD_LATEST_WEIGHTS_MANIFEST", "false").lower() == "true" 24 | ) 25 | self.weights_manifest = self._load_weights_manifest() 26 | self.weights_map = self._initialize_weights_map() 27 | 28 | def _load_weights_manifest(self): 29 | if self.download_latest_weights_manifest: 30 | self._download_updated_weights_manifest() 31 | return self._merge_manifests() 32 | 33 | def _download_updated_weights_manifest(self): 34 | if not os.path.exists(REMOTE_WEIGHTS_MANIFEST_PATH): 35 | print( 36 | f"Downloading updated weights manifest from {REMOTE_WEIGHTS_MANIFEST_URL}" 37 | ) 38 | start = time.time() 39 | try: 40 | subprocess.check_call( 41 | [ 42 | "pget", 43 | "--log-level", 44 | "warn", 45 | "-f", 46 | REMOTE_WEIGHTS_MANIFEST_URL, 47 | REMOTE_WEIGHTS_MANIFEST_PATH, 48 | ], 49 | close_fds=False, 50 | timeout=5, 51 | ) 52 | print( 53 | f"Downloading {REMOTE_WEIGHTS_MANIFEST_URL} took: {(time.time() - start):.2f}s" 54 | ) 55 | except subprocess.CalledProcessError: 56 | print(f"Failed to download {REMOTE_WEIGHTS_MANIFEST_URL}") 57 | pass 58 | except subprocess.TimeoutExpired: 59 | print(f"Download from {REMOTE_WEIGHTS_MANIFEST_URL} timed out") 60 | pass 61 | 62 | def _merge_manifests(self): 63 | if os.path.exists(WEIGHTS_MANIFEST_PATH): 64 | with open(WEIGHTS_MANIFEST_PATH, "r") as f: 65 | original_manifest = json.load(f) 66 | else: 67 | original_manifest = {} 68 | 69 | manifests_to_merge = [ 70 | REMOTE_WEIGHTS_MANIFEST_PATH, 71 | USER_WEIGHTS_MANIFEST_PATH, 72 | ] 73 | 74 | for manifest_path in manifests_to_merge: 75 | if os.path.exists(manifest_path): 76 | with open(manifest_path, "r") as f: 77 | manifest_to_merge = json.load(f) 78 | for key in manifest_to_merge: 79 | if key in original_manifest: 80 | for item in manifest_to_merge[key]: 81 | if item not in original_manifest[key]: 82 | print(f"Adding {item} to {key}") 83 | original_manifest[key].append(item) 84 | else: 85 | original_manifest[key] = manifest_to_merge[key] 86 | 87 | return original_manifest 88 | 89 | def _initialize_weights_map(self): 90 | weights_map = {} 91 | 92 | def generate_weights_map(keys, dest): 93 | # https://github.com/comfyanonymous/ComfyUI/commit/4f7a3cb6fbd58d7546b3c76ec1f418a2650ed709 94 | if dest == "unet": 95 | return { 96 | key: { 97 | "url": f"{BASE_URL}/{dest}/{key}.tar", 98 | "dest": f"{MODELS_PATH}/diffusion_models", 99 | } 100 | for key in keys 101 | } 102 | 103 | return { 104 | key: { 105 | "url": f"{BASE_URL}/{dest}/{key}.tar", 106 | "dest": f"{MODELS_PATH}/{dest}", 107 | } 108 | for key in keys 109 | } 110 | 111 | def update_weights_map(source_map): 112 | for k, v in source_map.items(): 113 | if k in weights_map: 114 | if isinstance(weights_map[k], list): 115 | weights_map[k].append(v) 116 | else: 117 | weights_map[k] = [weights_map[k], v] 118 | else: 119 | weights_map[k] = v 120 | 121 | for key in self.weights_manifest.keys(): 122 | directory_name = "LLM" if key == "LLM" else key.lower() 123 | map = generate_weights_map(self.weights_manifest[key], directory_name) 124 | update_weights_map(map) 125 | 126 | for module_name in dir(helpers): 127 | module = getattr(helpers, module_name) 128 | if hasattr(module, "weights_map"): 129 | map = module.weights_map(BASE_URL) 130 | update_weights_map(map) 131 | 132 | return weights_map 133 | 134 | def non_commercial_weights(self): 135 | return [ 136 | "cocoamixxl_v4Stable.safetensors", 137 | "copaxTimelessxlSDXL1_v8.safetensors", 138 | "epicrealismXL_v10.safetensors", 139 | "GPEN-BFR-1024.onnx", 140 | "GPEN-BFR-2048.onnx", 141 | "GPEN-BFR-512.onnx", 142 | "inswapper_128.onnx", 143 | "inswapper_128_fp16.onnx", 144 | "MODILL_XL_0.27_RC.safetensors", 145 | "proteus_v02.safetensors", 146 | "RealVisXL_V3.0_Turbo.safetensors", 147 | "RMBG-1.4/model.pth", 148 | "sd_xl_turbo_1.0.safetensors", 149 | "sd_xl_turbo_1.0_fp16.safetensors", 150 | "stable-cascade/effnet_encoder.safetensors", 151 | "stable-cascade/stage_a.safetensors", 152 | "stable-cascade/stage_b.safetensors", 153 | "stable-cascade/stage_b_bf16.safetensors", 154 | "stable-cascade/stage_b_lite.safetensors", 155 | "stable-cascade/stage_b_lite_bf16.safetensors", 156 | "stable-cascade/stage_c.safetensors", 157 | "stable-cascade/stage_c_bf16.safetensors", 158 | "stable-cascade/stage_c_lite.safetensors", 159 | "stable-cascade/stage_c_lite_bf16.safetensors", 160 | "stable_cascade_stage_b.safetensors", 161 | "stable_cascade_stage_c.safetensors", 162 | "svd.safetensors", 163 | "svd_xt.safetensors", 164 | "turbovisionxlSuperFastXLBasedOnNew_tvxlV32Bakedvae", 165 | ] 166 | 167 | def is_non_commercial_only(self, weight_str): 168 | return weight_str in self.non_commercial_weights() 169 | 170 | def get_weights_by_type(self, weight_type): 171 | return self.weights_manifest.get(weight_type, []) 172 | -------------------------------------------------------------------------------- /predict.py: -------------------------------------------------------------------------------- 1 | import os 2 | import mimetypes 3 | import json 4 | import shutil 5 | from typing import List 6 | from cog import BasePredictor, Input, Path 7 | from comfyui import ComfyUI 8 | from cog_model_helpers import optimise_images 9 | 10 | OUTPUT_DIR = "/tmp/outputs" 11 | INPUT_DIR = "/tmp/inputs" 12 | COMFYUI_TEMP_OUTPUT_DIR = "ComfyUI/temp" 13 | ALL_DIRECTORIES = [OUTPUT_DIR, INPUT_DIR, COMFYUI_TEMP_OUTPUT_DIR] 14 | 15 | mimetypes.add_type("image/webp", ".webp") 16 | 17 | # Save your example JSON to the same directory as predict.py 18 | api_json_file = "workflow_api.json" 19 | 20 | # Force HF offline 21 | os.environ["HF_DATASETS_OFFLINE"] = "1" 22 | os.environ["TRANSFORMERS_OFFLINE"] = "1" 23 | os.environ["HF_HUB_DISABLE_TELEMETRY"] = "1" 24 | 25 | 26 | class Predictor(BasePredictor): 27 | def setup(self): 28 | self.comfyUI = ComfyUI("127.0.0.1:8188") 29 | self.comfyUI.start_server(OUTPUT_DIR, INPUT_DIR) 30 | 31 | with open(api_json_file, "r") as file: 32 | workflow = json.loads(file.read()) 33 | self.comfyUI.handle_weights( 34 | workflow, 35 | weights_to_download=[ 36 | "face_yolov8n.pt", 37 | "appearance_feature_extractor.safetensors", 38 | "motion_extractor.safetensors", 39 | "spade_generator.safetensors", 40 | "stitching_retargeting_module.safetensors", 41 | "warping_module.safetensors", 42 | ], 43 | ) 44 | 45 | def filename_with_extension(self, input_file, prefix): 46 | extension = os.path.splitext(input_file.name)[1] 47 | return f"{prefix}{extension}" 48 | 49 | def handle_input_file( 50 | self, 51 | input_file: Path, 52 | filename: str = "image.png", 53 | ): 54 | shutil.copy(input_file, os.path.join(INPUT_DIR, filename)) 55 | 56 | def update_workflow(self, workflow, **kwargs): 57 | workflow["15"]["inputs"]["image"] = kwargs["image_filename"] 58 | 59 | expression_editor = workflow["14"]["inputs"] 60 | expression_editor["rotate_pitch"] = kwargs["rotate_pitch"] 61 | expression_editor["rotate_yaw"] = kwargs["rotate_yaw"] 62 | expression_editor["rotate_roll"] = kwargs["rotate_roll"] 63 | expression_editor["blink"] = kwargs["blink"] 64 | expression_editor["eyebrow"] = kwargs["eyebrow"] 65 | expression_editor["wink"] = kwargs["wink"] 66 | expression_editor["pupil_x"] = kwargs["pupil_x"] 67 | expression_editor["pupil_y"] = kwargs["pupil_y"] 68 | expression_editor["aaa"] = kwargs["aaa"] 69 | expression_editor["eee"] = kwargs["eee"] 70 | expression_editor["woo"] = kwargs["woo"] 71 | expression_editor["smile"] = kwargs["smile"] 72 | expression_editor["src_ratio"] = kwargs["src_ratio"] 73 | expression_editor["sample_ratio"] = kwargs["sample_ratio"] 74 | expression_editor["crop_factor"] = kwargs["crop_factor"] 75 | 76 | def predict( 77 | self, 78 | image: Path = Input( 79 | description="Image of a face", 80 | default=None, 81 | ), 82 | rotate_pitch: float = Input( 83 | default=0, 84 | ge=-20, 85 | le=20, 86 | description="Rotation pitch: Adjusts the up and down tilt of the face", 87 | ), 88 | rotate_yaw: float = Input( 89 | default=0, 90 | ge=-20, 91 | le=20, 92 | description="Rotation yaw: Adjusts the left and right turn of the face", 93 | ), 94 | rotate_roll: float = Input( 95 | default=0, 96 | ge=-20, 97 | le=20, 98 | description="Rotation roll: Adjusts the tilt of the face to the left or right", 99 | ), 100 | blink: float = Input( 101 | default=0, 102 | ge=-20, 103 | le=5, 104 | description="Blink: Controls the degree of eye closure", 105 | ), 106 | eyebrow: float = Input( 107 | default=0, 108 | ge=-10, 109 | le=15, 110 | description="Eyebrow: Adjusts the height and shape of the eyebrows", 111 | ), 112 | wink: float = Input( 113 | default=0, 114 | ge=0, 115 | le=25, 116 | description="Wink: Controls the degree of one eye closing", 117 | ), 118 | pupil_x: float = Input( 119 | default=0, 120 | ge=-15, 121 | le=15, 122 | description="Pupil X: Adjusts the horizontal position of the pupils", 123 | ), 124 | pupil_y: float = Input( 125 | default=0, 126 | ge=-15, 127 | le=15, 128 | description="Pupil Y: Adjusts the vertical position of the pupils", 129 | ), 130 | aaa: float = Input( 131 | default=0, 132 | ge=-30, 133 | le=120, 134 | description="AAA: Controls the mouth opening for 'aaa' sound", 135 | ), 136 | eee: float = Input( 137 | default=0, 138 | ge=-20, 139 | le=15, 140 | description="EEE: Controls the mouth shape for 'eee' sound", 141 | ), 142 | woo: float = Input( 143 | default=0, 144 | ge=-20, 145 | le=15, 146 | description="WOO: Controls the mouth shape for 'woo' sound", 147 | ), 148 | smile: float = Input( 149 | default=0, 150 | ge=-0.3, 151 | le=1.3, 152 | description="Smile: Adjusts the degree of smiling", 153 | ), 154 | src_ratio: float = Input(default=1, ge=0, le=1, description="Source ratio"), 155 | sample_ratio: float = Input( 156 | default=1, ge=-0.2, le=1.2, description="Sample ratio" 157 | ), 158 | crop_factor: float = Input( 159 | default=1.7, ge=1.5, le=2.5, description="Crop factor" 160 | ), 161 | output_format: str = optimise_images.predict_output_format(), 162 | output_quality: int = optimise_images.predict_output_quality(), 163 | ) -> List[Path]: 164 | """Run a single prediction on the model""" 165 | self.comfyUI.cleanup(ALL_DIRECTORIES) 166 | self.comfyUI.connect() 167 | 168 | image_filename = None 169 | if image: 170 | image_filename = self.filename_with_extension(image, "image") 171 | self.handle_input_file(image, image_filename) 172 | 173 | with open(api_json_file, "r") as file: 174 | workflow = json.loads(file.read()) 175 | 176 | self.update_workflow( 177 | workflow, 178 | image_filename=image_filename, 179 | rotate_pitch=rotate_pitch, 180 | rotate_yaw=rotate_yaw, 181 | rotate_roll=rotate_roll, 182 | blink=blink, 183 | eyebrow=eyebrow, 184 | wink=wink, 185 | pupil_x=pupil_x, 186 | pupil_y=pupil_y, 187 | aaa=aaa, 188 | eee=eee, 189 | woo=woo, 190 | smile=smile, 191 | src_ratio=src_ratio, 192 | sample_ratio=sample_ratio, 193 | crop_factor=crop_factor, 194 | ) 195 | 196 | self.comfyUI.run_workflow(workflow) 197 | 198 | return optimise_images.optimise_image_files( 199 | output_format, output_quality, self.comfyUI.get_files(COMFYUI_TEMP_OUTPUT_DIR) 200 | ) 201 | -------------------------------------------------------------------------------- /comfyui.py: -------------------------------------------------------------------------------- 1 | import os 2 | import urllib.request 3 | import subprocess 4 | import threading 5 | import time 6 | import json 7 | import urllib 8 | import uuid 9 | import websocket 10 | import random 11 | import requests 12 | import shutil 13 | import custom_node_helpers as helpers 14 | from cog import Path 15 | from node import Node 16 | from weights_downloader import WeightsDownloader 17 | from urllib.error import URLError 18 | 19 | 20 | class ComfyUI: 21 | def __init__(self, server_address): 22 | self.weights_downloader = WeightsDownloader() 23 | self.server_address = server_address 24 | 25 | def start_server(self, output_directory, input_directory): 26 | self.input_directory = input_directory 27 | self.output_directory = output_directory 28 | self.apply_helper_methods("prepare", weights_downloader=self.weights_downloader) 29 | 30 | start_time = time.time() 31 | server_thread = threading.Thread( 32 | target=self.run_server, args=(output_directory, input_directory) 33 | ) 34 | server_thread.start() 35 | while not self.is_server_running(): 36 | if time.time() - start_time > 60: 37 | raise TimeoutError("Server did not start within 60 seconds") 38 | time.sleep(0.5) 39 | 40 | elapsed_time = time.time() - start_time 41 | print(f"Server started in {elapsed_time:.2f} seconds") 42 | 43 | def run_server(self, output_directory, input_directory): 44 | command = f"python ./ComfyUI/main.py --output-directory {output_directory} --input-directory {input_directory} --disable-metadata" 45 | 46 | """ 47 | We need to capture the stdout and stderr from the server process 48 | so that we can print the logs to the console. If we don't do this 49 | then at the point where ComfyUI attempts to print it will throw a 50 | broken pipe error. This only happens from cog v0.9.13 onwards. 51 | """ 52 | server_process = subprocess.Popen( 53 | command, 54 | shell=True, 55 | stdout=subprocess.PIPE, 56 | stderr=subprocess.PIPE, 57 | universal_newlines=True, 58 | ) 59 | 60 | def print_stdout(): 61 | for stdout_line in iter(server_process.stdout.readline, ""): 62 | print(f"[ComfyUI] {stdout_line.strip()}") 63 | 64 | stdout_thread = threading.Thread(target=print_stdout) 65 | stdout_thread.start() 66 | 67 | for stderr_line in iter(server_process.stderr.readline, ""): 68 | print(f"[ComfyUI] {stderr_line.strip()}") 69 | 70 | def is_server_running(self): 71 | try: 72 | with urllib.request.urlopen( 73 | "http://{}/history/{}".format(self.server_address, "123") 74 | ) as response: 75 | return response.status == 200 76 | except URLError: 77 | return False 78 | 79 | def apply_helper_methods(self, method_name, *args, **kwargs): 80 | # Dynamically applies a method from helpers module with given args. 81 | # Example usage: self.apply_helper_methods("add_weights", weights_to_download, node) 82 | for module_name in dir(helpers): 83 | module = getattr(helpers, module_name) 84 | method = getattr(module, method_name, None) 85 | if callable(method): 86 | method(*args, **kwargs) 87 | 88 | def handle_weights(self, workflow, weights_to_download=None): 89 | if weights_to_download is None: 90 | weights_to_download = [] 91 | 92 | print("Checking weights") 93 | embeddings = self.weights_downloader.get_weights_by_type("EMBEDDINGS") 94 | embedding_to_fullname = {emb.split(".")[0]: emb for emb in embeddings} 95 | weights_filetypes = self.weights_downloader.supported_filetypes 96 | 97 | for node in workflow.values(): 98 | self.apply_helper_methods("add_weights", weights_to_download, Node(node)) 99 | 100 | for input in node["inputs"].values(): 101 | if isinstance(input, str): 102 | if any(key in input for key in embedding_to_fullname): 103 | weights_to_download.extend( 104 | embedding_to_fullname[key] 105 | for key in embedding_to_fullname 106 | if key in input 107 | ) 108 | elif any(input.endswith(ft) for ft in weights_filetypes): 109 | weights_to_download.append(input) 110 | 111 | weights_to_download = list(set(weights_to_download)) 112 | 113 | for weight in weights_to_download: 114 | self.weights_downloader.download_weights(weight) 115 | 116 | print("====================================") 117 | 118 | def is_image_or_video_value(self, value): 119 | filetypes = [".png", ".jpg", ".jpeg", ".webp", ".mp4", ".webm"] 120 | return isinstance(value, str) and any( 121 | value.lower().endswith(ft) for ft in filetypes 122 | ) 123 | 124 | def handle_known_unsupported_nodes(self, workflow): 125 | for node in workflow.values(): 126 | self.apply_helper_methods("check_for_unsupported_nodes", Node(node)) 127 | 128 | def handle_inputs(self, workflow): 129 | print("Checking inputs") 130 | seen_inputs = set() 131 | for node in workflow.values(): 132 | if "inputs" in node: 133 | for input_key, input_value in node["inputs"].items(): 134 | if isinstance(input_value, str) and input_value not in seen_inputs: 135 | seen_inputs.add(input_value) 136 | if input_value.startswith(("http://", "https://")): 137 | filename = os.path.join( 138 | self.input_directory, os.path.basename(input_value) 139 | ) 140 | if not os.path.exists(filename): 141 | print(f"Downloading {input_value} to {filename}") 142 | try: 143 | response = requests.get(input_value) 144 | response.raise_for_status() 145 | with open(filename, "wb") as file: 146 | file.write(response.content) 147 | print(f"✅ {filename}") 148 | except requests.exceptions.RequestException as e: 149 | print(f"❌ Error downloading {input_value}: {e}") 150 | 151 | # The same URL may be included in a workflow more than once 152 | node["inputs"][input_key] = filename 153 | 154 | elif self.is_image_or_video_value(input_value): 155 | filename = os.path.join( 156 | self.input_directory, os.path.basename(input_value) 157 | ) 158 | if not os.path.exists(filename): 159 | print(f"❌ {filename} not provided") 160 | else: 161 | print(f"✅ {filename}") 162 | 163 | print("====================================") 164 | 165 | def connect(self): 166 | self.client_id = str(uuid.uuid4()) 167 | self.ws = websocket.WebSocket() 168 | self.ws.connect(f"ws://{self.server_address}/ws?clientId={self.client_id}") 169 | 170 | def post_request(self, endpoint, data=None): 171 | url = f"http://{self.server_address}{endpoint}" 172 | headers = {"Content-Type": "application/json"} if data else {} 173 | json_data = json.dumps(data).encode("utf-8") if data else None 174 | req = urllib.request.Request( 175 | url, data=json_data, headers=headers, method="POST" 176 | ) 177 | with urllib.request.urlopen(req) as response: 178 | if response.status != 200: 179 | print(f"Failed: {endpoint}, status code: {response.status}") 180 | 181 | # https://github.com/comfyanonymous/ComfyUI/blob/master/server.py 182 | def clear_queue(self): 183 | self.post_request("/queue", {"clear": True}) 184 | self.post_request("/interrupt") 185 | 186 | def queue_prompt(self, prompt): 187 | try: 188 | # Prompt is the loaded workflow (prompt is the label comfyUI uses) 189 | p = {"prompt": prompt, "client_id": self.client_id} 190 | data = json.dumps(p).encode("utf-8") 191 | req = urllib.request.Request( 192 | f"http://{self.server_address}/prompt?{self.client_id}", data=data 193 | ) 194 | 195 | output = json.loads(urllib.request.urlopen(req).read()) 196 | return output["prompt_id"] 197 | except urllib.error.HTTPError as e: 198 | print(f"ComfyUI error: {e.code} {e.reason}") 199 | http_error = True 200 | 201 | if http_error: 202 | raise Exception( 203 | "ComfyUI Error – Your workflow could not be run. This usually happens if you’re trying to use an unsupported node. Check the logs for 'KeyError: ' details, and go to https://github.com/fofr/cog-comfyui to see the list of supported custom nodes." 204 | ) 205 | 206 | def wait_for_prompt_completion(self, workflow, prompt_id): 207 | while True: 208 | out = self.ws.recv() 209 | if isinstance(out, str): 210 | message = json.loads(out) 211 | 212 | if message["type"] == "execution_error": 213 | error_message = json.dumps(message, indent=2) 214 | raise Exception( 215 | f"There was an error executing your workflow:\n\n{error_message}" 216 | ) 217 | 218 | if message["type"] == "executing": 219 | data = message["data"] 220 | if data["node"] is None and data["prompt_id"] == prompt_id: 221 | break 222 | elif data["prompt_id"] == prompt_id: 223 | node = workflow.get(data["node"], {}) 224 | meta = node.get("_meta", {}) 225 | class_type = node.get("class_type", "Unknown") 226 | print( 227 | f"Executing node {data['node']}, title: {meta.get('title', 'Unknown')}, class type: {class_type}" 228 | ) 229 | else: 230 | continue 231 | 232 | def load_workflow(self, workflow): 233 | if not isinstance(workflow, dict): 234 | wf = json.loads(workflow) 235 | else: 236 | wf = workflow 237 | 238 | # There are two types of ComfyUI JSON 239 | # We need the API version 240 | if any(key in wf.keys() for key in ["last_node_id", "last_link_id", "version"]): 241 | raise ValueError( 242 | "You need to use the API JSON version of a ComfyUI workflow. To do this go to your ComfyUI settings and turn on 'Enable Dev mode Options'. Then you can save your ComfyUI workflow via the 'Save (API Format)' button." 243 | ) 244 | 245 | self.handle_known_unsupported_nodes(wf) 246 | self.handle_inputs(wf) 247 | self.handle_weights(wf) 248 | return wf 249 | 250 | def reset_execution_cache(self): 251 | print("Resetting execution cache") 252 | with open("reset.json", "r") as file: 253 | reset_workflow = json.loads(file.read()) 254 | self.queue_prompt(reset_workflow) 255 | 256 | def randomise_input_seed(self, input_key, inputs): 257 | if input_key in inputs and isinstance(inputs[input_key], (int, float)): 258 | new_seed = random.randint(0, 2**32 - 1) 259 | print(f"Randomising {input_key} to {new_seed}") 260 | inputs[input_key] = new_seed 261 | 262 | def randomise_seeds(self, workflow): 263 | for node_id, node in workflow.items(): 264 | inputs = node.get("inputs", {}) 265 | seed_keys = ["seed", "noise_seed", "rand_seed"] 266 | for seed_key in seed_keys: 267 | self.randomise_input_seed(seed_key, inputs) 268 | 269 | def run_workflow(self, workflow): 270 | print("Running workflow") 271 | prompt_id = self.queue_prompt(workflow) 272 | self.wait_for_prompt_completion(workflow, prompt_id) 273 | output_json = self.get_history(prompt_id) 274 | print("outputs: ", output_json) 275 | print("====================================") 276 | 277 | def get_history(self, prompt_id): 278 | with urllib.request.urlopen( 279 | f"http://{self.server_address}/history/{prompt_id}" 280 | ) as response: 281 | output = json.loads(response.read()) 282 | return output[prompt_id]["outputs"] 283 | 284 | def get_files(self, directories, prefix="", file_extensions=None): 285 | files = [] 286 | if isinstance(directories, str): 287 | directories = [directories] 288 | 289 | for directory in directories: 290 | for f in os.listdir(directory): 291 | if f == "__MACOSX": 292 | continue 293 | path = os.path.join(directory, f) 294 | if os.path.isfile(path): 295 | print(f"{prefix}{f}") 296 | files.append(Path(path)) 297 | elif os.path.isdir(path): 298 | print(f"{prefix}{f}/") 299 | files.extend(self.get_files(path, prefix=f"{prefix}{f}/")) 300 | 301 | if file_extensions: 302 | files = [f for f in files if f.name.split(".")[-1] in file_extensions] 303 | 304 | return sorted(files) 305 | 306 | def cleanup(self, directories): 307 | self.clear_queue() 308 | for directory in directories: 309 | if os.path.exists(directory): 310 | shutil.rmtree(directory) 311 | os.makedirs(directory) 312 | -------------------------------------------------------------------------------- /weights.json: -------------------------------------------------------------------------------- 1 | { 2 | "CHECKPOINTS": [ 3 | "512-inpainting-ema.safetensors", 4 | "absolutereality_v181.safetensors", 5 | "albedobaseXL_v13.safetensors", 6 | "albedobaseXL_v21.safetensors", 7 | "animagine-xl-3.0.safetensors", 8 | "anything-v3-fp16-pruned.safetensors", 9 | "aura_flow_0.1.safetensors", 10 | "aura_flow_0.2.safetensors", 11 | "aura_flow_0.3.safetensors", 12 | "CinematicRedmond.safetensors", 13 | "cocoamixxl_v4Stable.safetensors", 14 | "copaxCuteXLSDXL10_v4.safetensors", 15 | "copaxTimelessxlSDXL1_v122.safetensors", 16 | "copaxTimelessxlSDXL1_v8.safetensors", 17 | "crystalClearXL_ccxl.safetensors", 18 | "Deliberate_v2.safetensors", 19 | "disneyPixarCartoon_v10.safetensors", 20 | "dreamlabsoil_V2_v2.safetensors", 21 | "DreamShaper_6.2_BakedVae_pruned.safetensors", 22 | "DreamShaper_6.31_BakedVae.safetensors", 23 | "DreamShaper_6.31_BakedVae_pruned.safetensors", 24 | "DreamShaper_6.31_INPAINTING.inpainting.safetensors", 25 | "DreamShaper_6_BakedVae.safetensors", 26 | "dreamshaper_8.safetensors", 27 | "dreamshaper_8LCM.safetensors", 28 | "dreamshaperXL_alpha2Xl10.safetensors", 29 | "dreamshaperXL_lightningDPMSDE.safetensors", 30 | "dynavision_v20Bakedvae.safetensors", 31 | "epicrealism_naturalSinRC1VAE.safetensors", 32 | "epicrealism_pureEvolutionV5-inpainting.safetensors", 33 | "Epicrealismxl_Hades.safetensors", 34 | "epicrealismXL_v10.safetensors", 35 | "epicrealismXL_v7FinalDestination.safetensors", 36 | "Hyper-SDXL-1step-Unet-Comfyui.fp16.safetensors", 37 | "Hyper-SDXL-1step-Unet.safetensors", 38 | "imp_v10.safetensors", 39 | "jibMixRealisticXL_v10Lightning46Step.safetensors", 40 | "Juggernaut-XL_v9_RunDiffusionPhoto_v2.safetensors", 41 | "juggernaut_reborn.safetensors", 42 | "Juggernaut_RunDiffusionPhoto2_Lightning_4Steps.safetensors", 43 | "juggernautXL_juggernautX.safetensors", 44 | "juggernautXL_v8Rundiffusion.safetensors", 45 | "juggernautXL_v9Rdphoto2Lightning.safetensors", 46 | "juggerxlInpaint_juggerInpaintV8.safetensors", 47 | "LCM_Dreamshaper_v7_4k.safetensors", 48 | "leosamsHelloworldXL_helloworldXL60.safetensors", 49 | "magicmixReverie_v10.safetensors", 50 | "majicmixRealistic_v7.safetensors", 51 | "motionctrl.pth", 52 | "motionctrl_svd.ckpt", 53 | "photonLCM_v10.safetensors", 54 | "PixArt-Sigma-XL-2-1024-MS.pth", 55 | "pixlAnimeCartoonComic_v10.safetensors", 56 | "playground-v2.5-1024px-aesthetic.fp16.safetensors", 57 | "ponyDiffusionV6XL_v6StartWithThisOne.safetensors", 58 | "proteus_v02.safetensors", 59 | "ProteusV0.4-Lighting.safetensors", 60 | "ProteusV0.4.safetensors", 61 | "proteusV0.5.safetensors", 62 | "rcnzCartoon3d_v20.safetensors", 63 | "realismEngineSDXL_v30VAE.safetensors", 64 | "Realistic_Vision_V4.0-inpainting.safetensors", 65 | "Realistic_Vision_V4.0.safetensors", 66 | "Realistic_Vision_V5.1-inpainting.ckpt", 67 | "Realistic_Vision_V5.1-inpainting.safetensors", 68 | "Realistic_Vision_V5.1.ckpt", 69 | "Realistic_Vision_V5.1.safetensors", 70 | "Realistic_Vision_V5.1_fp16-no-ema-inpainting.ckpt", 71 | "Realistic_Vision_V5.1_fp16-no-ema-inpainting.safetensors", 72 | "Realistic_Vision_V5.1_fp16-no-ema.ckpt", 73 | "Realistic_Vision_V5.1_fp16-no-ema.safetensors", 74 | "Realistic_Vision_V6.0_NV_B1.safetensors", 75 | "Realistic_Vision_V6.0_NV_B1_fp16.safetensors", 76 | "Realistic_Vision_V6.0_NV_B1_inpainting.safetensors", 77 | "Realistic_Vision_V6.0_NV_B1_inpainting_fp16.safetensors", 78 | "realisticLCMBYStable_v10.safetensors", 79 | "realisticVisionV60B1_v51HyperVAE.safetensors", 80 | "RealVisXL_V2.0.safetensors", 81 | "RealVisXL_V3.0.safetensors", 82 | "RealVisXL_V3.0_Turbo.safetensors", 83 | "RealVisXL_V4.0.safetensors", 84 | "RealVisXL_V4.0_Lightning.safetensors", 85 | "rundiffusionXL_beta.safetensors", 86 | "sd3_medium.safetensors", 87 | "sd3_medium_incl_clips.safetensors", 88 | "sd3_medium_incl_clips_t5xxlfp16.safetensors", 89 | "sd3_medium_incl_clips_t5xxlfp8.safetensors", 90 | "sd_xl_base_1.0.safetensors", 91 | "sd_xl_base_1.0_0.9vae.safetensors", 92 | "sd_xl_refiner_1.0.safetensors", 93 | "sd_xl_refiner_1.0_0.9vae.safetensors", 94 | "sd_xl_turbo_1.0.safetensors", 95 | "sd_xl_turbo_1.0_fp16.safetensors", 96 | "SDXL-Flash.safetensors", 97 | "SDXL-Flash_Mini.safetensors", 98 | "sdxl_lightning_1step_x0.safetensors", 99 | "sdxl_lightning_2step.safetensors", 100 | "sdxl_lightning_4step.safetensors", 101 | "sdxl_lightning_8step.safetensors", 102 | "sdxlUnstableDiffusers_nihilmania.safetensors", 103 | "sdxlUnstableDiffusers_v11Rundiffusion.safetensors", 104 | "segmind-vega.safetensors", 105 | "SSD-1B.safetensors", 106 | "stable_cascade_stage_b.safetensors", 107 | "stable_cascade_stage_c.safetensors", 108 | "starlightXLAnimated_v3.safetensors", 109 | "SUPIR-v0F.ckpt", 110 | "SUPIR-v0F_fp16.safetensors", 111 | "SUPIR-v0Q.ckpt", 112 | "SUPIR-v0Q_fp16.safetensors", 113 | "svd.safetensors", 114 | "svd_xt.safetensors", 115 | "toonyou_beta6.safetensors", 116 | "turbovisionxlSuperFastXLBasedOnNew_tvxlV32Bakedvae.safetensors", 117 | "v1-5-pruned-emaonly.ckpt", 118 | "v2-1_512-ema-pruned.safetensors", 119 | "v2-1_768-ema-pruned.ckpt", 120 | "v2-1_768-ema-pruned.safetensors", 121 | "v2-1_768-nonema-pruned.ckpt", 122 | "v2-1_768-nonema-pruned.safetensors", 123 | "wd-illusion-fp16.safetensors", 124 | "x4-upscaler-ema.safetensors" 125 | ], 126 | "UPSCALE_MODELS": [ 127 | "4x-AnimeSharp.pth", 128 | "4x-UltraMix_Balanced.pth", 129 | "4x-UltraMix_Smooth.pth", 130 | "4x-UltraSharp.pth", 131 | "4x_foolhardy_Remacri.pth", 132 | "4x_NMKD-Siax_200k.pth", 133 | "8x_NMKD-Superscale_150000_G.pth", 134 | "ESRGAN_4x.pth", 135 | "RealESRGAN_x2.pth", 136 | "RealESRGAN_x4.pth", 137 | "RealESRGAN_x4plus.pth", 138 | "RealESRGAN_x4plus_anime_6B.pth", 139 | "RealESRGAN_x8.pth" 140 | ], 141 | "CLIP": [ 142 | "clip_g.safetensors", 143 | "clip_l.safetensors", 144 | "models--QuanSun--EVA-CLIP", 145 | "sd15/model.fp16.safetensors", 146 | "sd15/model.safetensors", 147 | "stable-diffusion-2-1-clip-fp16.safetensors", 148 | "stable-diffusion-2-1-clip.safetensors", 149 | "t5xxl_fp16.safetensors", 150 | "t5xxl_fp8_e4m3fn.safetensors", 151 | "ViT-L-14-TEXT-detail-improved-hiT-GmP-TE-only-HF.safetensors" 152 | ], 153 | "CLIP_VISION": [ 154 | "CLIP-ViT-bigG-14-laion2B-39B-b160k.safetensors", 155 | "CLIP-ViT-H-14-laion2B-s32B-b79K.safetensors", 156 | "CLIP-ViT-H-fp16.safetensors", 157 | "clip-vit-large-patch14-336.bin", 158 | "clip-vit-large-patch14.bin", 159 | "clip_vision_g.safetensors", 160 | "IPAdapter_image_encoder_sd15.safetensors", 161 | "model.15.safetensors", 162 | "model.sdxl.safetensors" 163 | ], 164 | "LORAS": [ 165 | "3d_render_style_xl.safetensors", 166 | "add-detail-xl.safetensors", 167 | "aesthetic_anime_v1s.safetensors", 168 | "age_slider-LECO-v1.safetensors", 169 | "AnimateLCM_sd15_t2v_lora.safetensors", 170 | "araminta_k/abstract-anime-painting.safetensors", 171 | "araminta_k/araminta-k-illustration.safetensors", 172 | "araminta_k/BandW-Manga.safetensors", 173 | "araminta_k/flux_dev_frostinglane_araminta_k.safetensors", 174 | "araminta_k/gemini-anime.safetensors", 175 | "araminta_k/haute-couture-illustration.safetensors", 176 | "araminta_k/japanese-poster-prints.safetensors", 177 | "araminta_k/lost-and-lonely-manga.safetensors", 178 | "araminta_k/painting-light.safetensors", 179 | "araminta_k/pop-art-anime.safetensors", 180 | "araminta_k/sketched-out-manga.safetensors", 181 | "araminta_k/soft-ones.safetensors", 182 | "araminta_k/soft-pastel-anime.safetensors", 183 | "araminta_k/vincent-sketch-manga.safetensors", 184 | "arcane_offset.safetensors", 185 | "artificialguybr/3DRedmond-3DRenderStyle-3DRenderAF.safetensors", 186 | "artificialguybr/3DRedmond15V-LiberteRedmond-3DRenderStyle-3DRenderAF.safetensors", 187 | "artificialguybr/AnalogRedmond-AnalogRedmAF.safetensors", 188 | "artificialguybr/AnalogRedmondV2-Analog-AnalogRedmAF.safetensors", 189 | "artificialguybr/BetterTextRedmond.safetensors", 190 | "artificialguybr/ClayAnimationRedm.safetensors", 191 | "artificialguybr/ClayAnimationRedmond15-ClayAnimation-Clay.safetensors", 192 | "artificialguybr/ColoringBookRedmond-ColoringBook-ColoringBookAF.safetensors", 193 | "artificialguybr/ColoringBookRedmond-ColoringBookAF.safetensors", 194 | "artificialguybr/ColoringBookRedmond21V-FreedomRedmond-ColoringBook-ColoringBookAF.safetensors", 195 | "artificialguybr/CuteCartoon15V-LiberteRedmodModel-Cartoon-CuteCartoonAF.safetensors", 196 | "artificialguybr/CuteCartoonRedmond-CuteCartoon-CuteCartoonAF.safetensors", 197 | "artificialguybr/CuteFruitsRedmond-CtFruitsRedmAF.safetensors", 198 | "artificialguybr/FilmGrainRedmond-FilmGrain-FilmGrainAF.safetensors", 199 | "artificialguybr/IconsRedmond.safetensors", 200 | "artificialguybr/IconsRedmond15V-Icons.safetensors", 201 | "artificialguybr/IconsRedmondV2-Icons.safetensors", 202 | "artificialguybr/LineAniRedmond-LineAniAF.safetensors", 203 | "artificialguybr/LineAniRedmondV2-Lineart-LineAniAF.safetensors", 204 | "artificialguybr/LogoRedmond15V-LogoRedmAF-Logo.safetensors", 205 | "artificialguybr/LogoRedmond_LogoRedAF.safetensors", 206 | "artificialguybr/LogoRedmondV2-Logo-LogoRedmAF.safetensors", 207 | "artificialguybr/MoviePosterRedmond-MoviePoster-MoviePosterRedAF.safetensors", 208 | "artificialguybr/PixelArtRedmond-Lite64.safetensors", 209 | "artificialguybr/PixelArtRedmond15V-PixelArt-PIXARFK.safetensors", 210 | "artificialguybr/PomologicalWatercolorRedmond.safetensors", 211 | "artificialguybr/PS1Redmond-PS1Game-Playstation1Graphics.safetensors", 212 | "artificialguybr/StickersRedmond.safetensors", 213 | "artificialguybr/StickersRedmond15Version-Stickers-Sticker.safetensors", 214 | "artificialguybr/StickersRedmond21V-FreedomRedmond-Sticker-Stickers.safetensors", 215 | "artificialguybr/StoryBookRedmond-KidsRedmAF.safetensors", 216 | "artificialguybr/StoryBookRedmond15-KidsRedmAF-KidsBook.safetensors", 217 | "artificialguybr/StorybookRedmondUnbound-KidsRedmAF.safetensors", 218 | "artificialguybr/StorybookRedmondV2-KidsBook-KidsRedmAF.safetensors", 219 | "artificialguybr/StudioGhibli.Redmond-StdGBRRedmAF-StudioGhibli.safetensors", 220 | "artificialguybr/StudioGhibliRedmond-StdGBRedmAF.safetensors", 221 | "artificialguybr/ToyRedmond-FnkRedmAF.safetensors", 222 | "artificialguybr/TshirtDesignRedmond-TshirtDesignAF.safetensors", 223 | "artificialguybr/TShirtDesignRedmondV2-Tshirtdesign-TshirtDesignAF.safetensors", 224 | "artificialguybr/View360.safetensors", 225 | "b_lora/B-LoRA-cartoon_line.safetensors", 226 | "b_lora/B-LoRA-ink_sketch.safetensors", 227 | "b_lora/B-LoRA-pen_sketch.safetensors", 228 | "b_lora/B_LoRA_colorful-squirrel.safetensors", 229 | "b_lora/B_LoRA_pencil_boy.safetensors", 230 | "badquality_v02.safetensors", 231 | "charTurnBetaLora.safetensors", 232 | "COOLKIDS_MERGE_V2.5.safetensors", 233 | "Cyberpunk_typeface_flux_lora_v1.safetensors", 234 | "flux-RealismLora.safetensors", 235 | "fofr/emoji.safetensors", 236 | "glowneon_xl_v1.safetensors", 237 | "Harrlogos_v2.0.safetensors", 238 | "hd_helper_v1.safetensors", 239 | "Hyper-FLUX.1-dev-16steps-lora.safetensors", 240 | "Hyper-FLUX.1-dev-8steps-lora.safetensors", 241 | "Hyper-SD15-1step-lora.safetensors", 242 | "Hyper-SD15-2steps-lora.safetensors", 243 | "Hyper-SD15-4steps-lora.safetensors", 244 | "Hyper-SD15-8steps-lora.safetensors", 245 | "Hyper-SDXL-1step-lora.safetensors", 246 | "Hyper-SDXL-2steps-lora.safetensors", 247 | "Hyper-SDXL-4steps-lora.safetensors", 248 | "Hyper-SDXL-8steps-lora.safetensors", 249 | "ip-adapter-faceid-plus_sd15_lora.safetensors", 250 | "ip-adapter-faceid-plusv2_sd15_lora.safetensors", 251 | "ip-adapter-faceid-plusv2_sdxl_lora.safetensors", 252 | "ip-adapter-faceid_sd15_lora.safetensors", 253 | "ip-adapter-faceid_sdxl_lora.safetensors", 254 | "lcm-lora-sdv1-5.safetensors", 255 | "lcm-lora-ssd-1b.safetensors", 256 | "lcm_lora_sdxl.safetensors", 257 | "MODILL_XL_0.27_RC.safetensors", 258 | "more_details.safetensors", 259 | "PerfectEyesXL.safetensors", 260 | "Polaroidv2.safetensors", 261 | "RW_PS1v1.safetensors", 262 | "sd_xl_offset_example-lora_1.0.safetensors", 263 | "sdxl_lightning_2step_lora.pth", 264 | "sdxl_lightning_2step_lora.safetensors", 265 | "sdxl_lightning_4step_lora.pth", 266 | "sdxl_lightning_4step_lora.safetensors", 267 | "sdxl_lightning_8step_lora.pth", 268 | "sdxl_lightning_8step_lora.safetensors", 269 | "SDXLrender_v2.0.safetensors", 270 | "Segmind-VegaRT.safetensors", 271 | "theovercomer8sContrastFix_sd15.safetensors", 272 | "theovercomer8sContrastFix_sd21768.safetensors", 273 | "weight_slider-LECO-v1.safetensors" 274 | ], 275 | "EMBEDDINGS": [ 276 | "bad_prompt_version2-neg.pt", 277 | "easynegative.safetensors", 278 | "epiCNegative.pt", 279 | "epiCRealism.pt", 280 | "FastNegativeV2.pt", 281 | "JuggernautNegative-neg.pt", 282 | "negative_hand-neg.pt", 283 | "ng_deepnegative_v1_75t.pt", 284 | "realisticvision-negative-embedding.pt", 285 | "verybadimagenegative_v1.3.pt" 286 | ], 287 | "CONTROLNET": [ 288 | "animatediff_controlnet.ckpt", 289 | "control-lora-canny-rank128.safetensors", 290 | "control-lora-canny-rank256.safetensors", 291 | "control-lora-depth-rank128.safetensors", 292 | "control-lora-depth-rank256.safetensors", 293 | "control-lora-recolor-rank128.safetensors", 294 | "control-lora-recolor-rank256.safetensors", 295 | "control-lora-sketch-rank128-metadata.safetensors", 296 | "control-lora-sketch-rank256.safetensors", 297 | "control_boxdepth_LooseControlfp16.safetensors", 298 | "control_lora_rank128_v11e_sd15_ip2p_fp16.safetensors", 299 | "control_lora_rank128_v11e_sd15_shuffle_fp16.safetensors", 300 | "control_lora_rank128_v11f1e_sd15_tile_fp16.safetensors", 301 | "control_lora_rank128_v11f1p_sd15_depth_fp16.safetensors", 302 | "control_lora_rank128_v11p_sd15_canny_fp16.safetensors", 303 | "control_lora_rank128_v11p_sd15_inpaint_fp16.safetensors", 304 | "control_lora_rank128_v11p_sd15_lineart_fp16.safetensors", 305 | "control_lora_rank128_v11p_sd15_mlsd_fp16.safetensors", 306 | "control_lora_rank128_v11p_sd15_normalbae_fp16.safetensors", 307 | "control_lora_rank128_v11p_sd15_openpose_fp16.safetensors", 308 | "control_lora_rank128_v11p_sd15_scribble_fp16.safetensors", 309 | "control_lora_rank128_v11p_sd15_seg_fp16.safetensors", 310 | "control_lora_rank128_v11p_sd15_softedge_fp16.safetensors", 311 | "control_lora_rank128_v11p_sd15s2_lineart_anime_fp16.safetensors", 312 | "control_sd15_inpaint_depth_hand_fp16.safetensors", 313 | "control_v11e_sd15_ip2p.pth", 314 | "control_v11e_sd15_ip2p_fp16.safetensors", 315 | "control_v11e_sd15_shuffle.pth", 316 | "control_v11e_sd15_shuffle_fp16.safetensors", 317 | "control_v11f1e_sd15_tile.pth", 318 | "control_v11f1e_sd15_tile_fp16.safetensors", 319 | "control_v11f1p_sd15_depth.pth", 320 | "control_v11f1p_sd15_depth_fp16.safetensors", 321 | "control_v11p_sd15_canny.pth", 322 | "control_v11p_sd15_canny_fp16.safetensors", 323 | "control_v11p_sd15_inpaint.pth", 324 | "control_v11p_sd15_inpaint_fp16.safetensors", 325 | "control_v11p_sd15_lineart.pth", 326 | "control_v11p_sd15_lineart_fp16.safetensors", 327 | "control_v11p_sd15_mlsd.pth", 328 | "control_v11p_sd15_mlsd_fp16.safetensors", 329 | "control_v11p_sd15_normalbae.pth", 330 | "control_v11p_sd15_normalbae_fp16.safetensors", 331 | "control_v11p_sd15_openpose.pth", 332 | "control_v11p_sd15_openpose_fp16.safetensors", 333 | "control_v11p_sd15_scribble.pth", 334 | "control_v11p_sd15_scribble_fp16.safetensors", 335 | "control_v11p_sd15_seg.pth", 336 | "control_v11p_sd15_seg_fp16.safetensors", 337 | "control_v11p_sd15_softedge.pth", 338 | "control_v11p_sd15_softedge_fp16.safetensors", 339 | "control_v11p_sd15s2_lineart_anime.pth", 340 | "control_v11p_sd15s2_lineart_anime_fp16.safetensors", 341 | "control_v11u_sd15_tile_fp16.safetensors", 342 | "control_v1p_sd15_qrcode_monster.safetensors", 343 | "control_v1p_sdxl_qrcode_monster.safetensors", 344 | "controllllite_v01032064e_sdxl_canny_anime.safetensors", 345 | "controlnet-canny-sdxl-1.0.fp16.safetensors", 346 | "controlnet-canny-sdxl-1.0.safetensors", 347 | "controlnet-canny-sdxl-1.0_V2.safetensors", 348 | "controlnet-depth-sdxl-1.0.fp16.safetensors", 349 | "controlnet-depth-sdxl-1.0.safetensors", 350 | "controlnet-openpose-sdxl-1.0.safetensors", 351 | "controlnet-openpose-sdxl-1.0_twins.safetensors", 352 | "controlnet-scribble-sdxl-1.0.safetensors", 353 | "controlnet-sd-xl-1.0-softedge-dexined.safetensors", 354 | "controlnet-temporalnet-sdxl-1.0.safetensors", 355 | "controlnet-tile-sdxl-1.0.safetensors", 356 | "controlnet-union-promax-sdxl-1.0.safetensors", 357 | "controlnet-union-sdxl-1.0.safetensors", 358 | "controlnet-zoe-depth-sdxl-1.0.safetensors", 359 | "depth-anything.safetensors", 360 | "depth-zoe-xl-v1.0-controlnet.safetensors", 361 | "diffusers_xl_canny_full.safetensors", 362 | "diffusers_xl_canny_mid.safetensors", 363 | "diffusers_xl_canny_small.safetensors", 364 | "diffusers_xl_depth_full.safetensors", 365 | "diffusers_xl_depth_mid.safetensors", 366 | "diffusers_xl_depth_small.safetensors", 367 | "FLUX.1-dev-ControlNet-Union-Pro.safetensors", 368 | "flux.1-dev-controlnet-union.safetensors", 369 | "instantid-controlnet.safetensors", 370 | "ioclab_sd15_recolor.safetensors", 371 | "ip-adapter_sd15.pth", 372 | "ip-adapter_sd15_plus.pth", 373 | "ip-adapter_xl.pth", 374 | "kohya_controllllite_xl_blur.safetensors", 375 | "kohya_controllllite_xl_blur_anime.safetensors", 376 | "kohya_controllllite_xl_blur_anime_beta.safetensors", 377 | "kohya_controllllite_xl_canny.safetensors", 378 | "kohya_controllllite_xl_canny_anime.safetensors", 379 | "kohya_controllllite_xl_depth.safetensors", 380 | "kohya_controllllite_xl_depth_anime.safetensors", 381 | "kohya_controllllite_xl_openpose_anime.safetensors", 382 | "kohya_controllllite_xl_openpose_anime_v2.safetensors", 383 | "kohya_controllllite_xl_scribble_anime.safetensors", 384 | "mistoLine_fp16.safetensors", 385 | "mistoLine_rank256.safetensors", 386 | "OpenPoseXL2.safetensors", 387 | "sai_xl_canny_128lora.safetensors", 388 | "sai_xl_canny_256lora.safetensors", 389 | "sai_xl_depth_128lora.safetensors", 390 | "sai_xl_depth_256lora.safetensors", 391 | "sai_xl_recolor_128lora.safetensors", 392 | "sai_xl_recolor_256lora.safetensors", 393 | "sai_xl_sketch_128lora.safetensors", 394 | "sai_xl_sketch_256lora.safetensors", 395 | "sargezt_xl_depth.safetensors", 396 | "sargezt_xl_depth_faid_vidit.safetensors", 397 | "sargezt_xl_depth_zeed.safetensors", 398 | "sargezt_xl_softedge.safetensors", 399 | "SD3-Controlnet-Canny.safetensors", 400 | "SD3-Controlnet-Pose.safetensors", 401 | "SD3-Controlnet-Tile.safetensors", 402 | "t2i-adapter_diffusers_xl_canny.safetensors", 403 | "t2i-adapter_diffusers_xl_depth_midas.safetensors", 404 | "t2i-adapter_diffusers_xl_depth_zoe.safetensors", 405 | "t2i-adapter_diffusers_xl_lineart.safetensors", 406 | "t2i-adapter_diffusers_xl_openpose.safetensors", 407 | "t2i-adapter_diffusers_xl_sketch.safetensors", 408 | "t2i-adapter_xl_canny.safetensors", 409 | "t2i-adapter_xl_openpose.safetensors", 410 | "t2i-adapter_xl_sketch.safetensors", 411 | "t2iadapter_canny_sd14v1.pth", 412 | "t2iadapter_color_sd14v1.pth", 413 | "t2iadapter_depth_sd14v1.pth", 414 | "t2iadapter_keypose_sd14v1.pth", 415 | "t2iadapter_openpose_sd14v1.pth", 416 | "t2iadapter_seg_sd14v1.pth", 417 | "t2iadapter_sketch_sd14v1.pth", 418 | "t2iadapter_style_sd14v1.pth", 419 | "temporalnetversion2.ckpt", 420 | "thibaud_xl_openpose.safetensors", 421 | "thibaud_xl_openpose_256lora.safetensors", 422 | "TTPLANET_Controlnet_Tile_realistic_v2_fp16.safetensors", 423 | "TTPLANET_Controlnet_Tile_realistic_v2_rank256.safetensors" 424 | ], 425 | "IPADAPTER": [ 426 | "ip-adapter-faceid-plus_sd15.bin", 427 | "ip-adapter-faceid-plusv2_sd15.bin", 428 | "ip-adapter-faceid-plusv2_sdxl.bin", 429 | "ip-adapter-faceid-portrait-v11_sd15.bin", 430 | "ip-adapter-faceid-portrait_sd15.bin", 431 | "ip-adapter-faceid-portrait_sdxl.bin", 432 | "ip-adapter-faceid-portrait_sdxl_unnorm.bin", 433 | "ip-adapter-faceid_sd15.bin", 434 | "ip-adapter-faceid_sdxl.bin", 435 | "ip-adapter-full-face_sd15.bin", 436 | "ip-adapter-full-face_sd15.safetensors", 437 | "ip-adapter-plus-face_sd15.bin", 438 | "ip-adapter-plus-face_sd15.safetensors", 439 | "ip-adapter-plus-face_sdxl_vit-h.bin", 440 | "ip-adapter-plus-face_sdxl_vit-h.safetensors", 441 | "ip-adapter-plus_sd15.bin", 442 | "ip-adapter-plus_sd15.safetensors", 443 | "ip-adapter-plus_sdxl_vit-h.bin", 444 | "ip-adapter-plus_sdxl_vit-h.safetensors", 445 | "ip-adapter_sd15.bin", 446 | "ip-adapter_sd15.safetensors", 447 | "ip-adapter_sd15_light.bin", 448 | "ip-adapter_sd15_light.safetensors", 449 | "ip-adapter_sd15_light_v11.bin", 450 | "ip-adapter_sd15_vit-G.bin", 451 | "ip-adapter_sd15_vit-G.safetensors", 452 | "ip-adapter_sdxl.safetensors", 453 | "ip-adapter_sdxl_vit-h.safetensors", 454 | "ip_plus_composition_sd15.safetensors", 455 | "ip_plus_composition_sdxl.safetensors", 456 | "Kolors-IP-Adapter-Plus.bin" 457 | ], 458 | "VAE": [ 459 | "ae.safetensors", 460 | "sdxl_vae.safetensors", 461 | "stable-cascade/effnet_encoder.safetensors", 462 | "stable-cascade/stage_a.safetensors", 463 | "vae-ft-mse-840000-ema-pruned.safetensors" 464 | ], 465 | "UNET": [ 466 | "flux1-dev.safetensors", 467 | "flux1-schnell.safetensors", 468 | "iclight_sd15_fbc.safetensors", 469 | "iclight_sd15_fbc_unet_ldm.safetensors", 470 | "iclight_sd15_fc.safetensors", 471 | "iclight_sd15_fc_unet_ldm.safetensors", 472 | "iclight_sd15_fcon.safetensors", 473 | "kolors.fp16.safetensors", 474 | "sdxl_lightning_1step_unet_x0.pth", 475 | "sdxl_lightning_1step_unet_x0.safetensors", 476 | "sdxl_lightning_2step_unet.pth", 477 | "sdxl_lightning_2step_unet.safetensors", 478 | "sdxl_lightning_4step_unet.pth", 479 | "sdxl_lightning_4step_unet.safetensors", 480 | "sdxl_lightning_8step_unet.pth", 481 | "sdxl_lightning_8step_unet.safetensors", 482 | "stable-cascade/stage_b.safetensors", 483 | "stable-cascade/stage_b_bf16.safetensors", 484 | "stable-cascade/stage_b_lite.safetensors", 485 | "stable-cascade/stage_b_lite_bf16.safetensors", 486 | "stable-cascade/stage_c.safetensors", 487 | "stable-cascade/stage_c_bf16.safetensors", 488 | "stable-cascade/stage_c_lite.safetensors", 489 | "stable-cascade/stage_c_lite_bf16.safetensors" 490 | ], 491 | "DIFFUSION_MODELS": [ 492 | "flux1-dev-fp8.safetensors" 493 | ], 494 | "PHOTOMAKER": [ 495 | "photomaker-v1.bin", 496 | "photomaker-v2.bin" 497 | ], 498 | "INSTANTID": [ 499 | "instantid-ip-adapter.bin" 500 | ], 501 | "INSIGHTFACE": [ 502 | "antelopev2", 503 | "buffalo_l", 504 | "inswapper_128.onnx", 505 | "inswapper_128_fp16.onnx", 506 | "models/antelopev2", 507 | "models/buffalo_l" 508 | ], 509 | "FACEDETECTION": [ 510 | "detection_mobilenet0.25_Final.pth", 511 | "detection_Resnet50_Final.pth", 512 | "parsing_bisenet.pth", 513 | "parsing_parsenet.pth", 514 | "yolov5l-face.pth", 515 | "yolov5n-face.pth" 516 | ], 517 | "FACERESTORE_MODELS": [ 518 | "codeformer.pth", 519 | "GFPGANv1.3.pth", 520 | "GFPGANv1.4.pth", 521 | "GPEN-BFR-1024.onnx", 522 | "GPEN-BFR-2048.onnx", 523 | "GPEN-BFR-512.onnx", 524 | "RestoreFormer.pth" 525 | ], 526 | "MMDETS": [ 527 | "bbox/mmdet_anime-face_yolov3.pth" 528 | ], 529 | "SAMS": [ 530 | "mobile_sam.pt", 531 | "sam_hq_vit_b.pth", 532 | "sam_hq_vit_h.pth", 533 | "sam_hq_vit_l.pth", 534 | "sam_vit_b_01ec64.pth", 535 | "sam_vit_h_4b8939.pth", 536 | "sam_vit_l_0b3195.pth" 537 | ], 538 | "GROUNDING-DINO": [ 539 | "groundingdino_swinb_cogcoor.pth", 540 | "groundingdino_swint_ogc.pth" 541 | ], 542 | "BERT-BASE-UNCASED": [ 543 | "bert-base-uncased" 544 | ], 545 | "ULTRALYTICS": [ 546 | "bbox/Eyes.pt", 547 | "bbox/face_yolov8m.pt", 548 | "bbox/face_yolov8n.pt", 549 | "bbox/face_yolov8n.pt", 550 | "bbox/face_yolov8n_v2.pt", 551 | "bbox/face_yolov8s.pt", 552 | "bbox/hand_yolov8n.pt", 553 | "bbox/hand_yolov8s.pt", 554 | "segm/deepfashion2_yolov8s-seg.pt", 555 | "segm/face_yolov8m-seg_60.pt", 556 | "segm/face_yolov8n-seg2_60.pt", 557 | "segm/hair_yolov8n-seg_60.pt", 558 | "segm/hair_yolov8n-seg_60.pt", 559 | "segm/person_yolov8m-seg.pt", 560 | "segm/person_yolov8n-seg.pt", 561 | "segm/person_yolov8s-seg.pt", 562 | "segm/skin_yolov8m-seg_400.pt", 563 | "segm/skin_yolov8n-seg_400.pt", 564 | "segm/skin_yolov8n-seg_800.pt", 565 | "face_yolov8n.pt" 566 | ], 567 | "LAYER_MODEL": [ 568 | "layer_sd15_bg2fg.safetensors", 569 | "layer_sd15_fg2bg.safetensors", 570 | "layer_sd15_joint.safetensors", 571 | "layer_sd15_transparent_attn.safetensors", 572 | "layer_sd15_vae_transparent_decoder.safetensors", 573 | "layer_xl_bg2ble.safetensors", 574 | "layer_xl_bgble2fg.safetensors", 575 | "layer_xl_fg2ble.safetensors", 576 | "layer_xl_fgble2bg.safetensors", 577 | "layer_xl_transparent_attn.safetensors", 578 | "layer_xl_transparent_conv.safetensors", 579 | "vae_transparent_decoder.safetensors" 580 | ], 581 | "CLIPSEG": [ 582 | "models--CIDAS--clipseg-rd64-refined" 583 | ], 584 | "REMBG": [ 585 | "isnet-anime.onnx", 586 | "isnet-general-use.onnx", 587 | "silueta.onnx", 588 | "u2net.onnx", 589 | "u2net_cloth_seg.onnx", 590 | "u2net_human_seg.onnx", 591 | "u2netp.onnx", 592 | "vit_b-decoder-quant.onnx", 593 | "vit_b-encoder-quant.onnx" 594 | ], 595 | "PULID": [ 596 | "ip-adapter_pulid_sdxl_fp16.safetensors" 597 | ], 598 | "GLIGEN": [ 599 | "gligen_sd14_textbox_pruned.safetensors", 600 | "gligen_sd14_textbox_pruned_fp16.safetensors" 601 | ], 602 | "DYNAMICRAFTER": [ 603 | "tooncrafter_512_interp-fp16.safetensors" 604 | ], 605 | "ANIMATEDIFF_MODELS": [ 606 | "animatediff_lightning_1step_comfyui.safetensors", 607 | "animatediff_lightning_1step_diffusers.safetensors", 608 | "animatediff_lightning_2step_comfyui.safetensors", 609 | "animatediff_lightning_2step_diffusers.safetensors", 610 | "animatediff_lightning_4step_comfyui.safetensors", 611 | "animatediff_lightning_4step_diffusers.safetensors", 612 | "animatediff_lightning_8step_comfyui.safetensors", 613 | "animatediff_lightning_8step_diffusers.safetensors", 614 | "animatediffLCMMotion_v10.ckpt", 615 | "AnimateLCM_sd15_t2v.ckpt", 616 | "CameraCtrl_pruned.safetensors", 617 | "hsxl_temporal_layers.f16.safetensors", 618 | "hsxl_temporal_layers.safetensors", 619 | "lt_long_mm_16_64_frames.ckpt", 620 | "lt_long_mm_16_64_frames_v1.1.ckpt", 621 | "lt_long_mm_32_frames.ckpt", 622 | "mm-Stabilized_high.pth", 623 | "mm-Stabilized_mid.pth", 624 | "mm_sd_v14.ckpt", 625 | "mm_sd_v15.ckpt", 626 | "mm_sd_v15_v2.ckpt", 627 | "mm_sdxl_v10_beta.ckpt", 628 | "sd15_t2v_beta.ckpt", 629 | "temporaldiff-v1-animatediff.ckpt", 630 | "v3_sd15_adapter.ckpt", 631 | "v3_sd15_mm.ckpt", 632 | "v3_sd15_sparsectrl_rgb.ckpt", 633 | "v3_sd15_sparsectrl_scribble.ckpt" 634 | ], 635 | "ANIMATEDIFF_MOTION_LORA": [ 636 | "shatterAnimatediff_v10.safetensors", 637 | "v2_lora_PanLeft.ckpt", 638 | "v2_lora_PanRight.ckpt", 639 | "v2_lora_RollingAnticlockwise.ckpt", 640 | "v2_lora_RollingClockwise.ckpt", 641 | "v2_lora_TiltDown.ckpt", 642 | "v2_lora_TiltUp.ckpt", 643 | "v2_lora_ZoomIn.ckpt", 644 | "v2_lora_ZoomOut.ckpt" 645 | ], 646 | "LIVEPORTRAIT": [ 647 | "appearance_feature_extractor.safetensors", 648 | "landmark.onnx", 649 | "motion_extractor.safetensors", 650 | "spade_generator.safetensors", 651 | "stitching_retargeting_module.safetensors", 652 | "warping_module.safetensors" 653 | ], 654 | "LLM": [ 655 | "checkpoints/chatglm3-4bit.safetensors", 656 | "checkpoints/chatglm3-8bit.safetensors", 657 | "checkpoints/chatglm3-fp16.safetensors" 658 | ], 659 | "TENSORRT": [ 660 | "dreamshaperXL_lightningDPMSDE_DYN_A40-b-1-1-1-h-512-1536-1024-w-512-1536-1024.engine", 661 | "sd3_medium_DYN_A100-b-1-1-1-h-512-1536-1024-w-512-1536-1024.engine", 662 | "sd3_medium_DYN_A40-b-1-1-1-h-512-1536-1024-w-512-1536-1024.engine", 663 | "sd3_medium_DYN_H100-b-1-1-1-h-512-1536-1024-w-512-1536-1024.engine" 664 | ], 665 | "DIFFUSERS": [ 666 | "Kolors" 667 | ], 668 | "ULTRAPIXEL": [ 669 | "ultrapixel_t2i.safetensors" 670 | ], 671 | "INPAINT": [ 672 | "big-lama.pt", 673 | "brushnet_random_mask_brushnet_ckpt_sdxl_v0.safetensors", 674 | "brushnet_random_mask_fp16.safetensors", 675 | "brushnet_segmentation_mask_fp16.safetensors", 676 | "fooocus_inpaint_head.pth", 677 | "fooocus_lama.safetensors", 678 | "inpaint.fooocus.patch", 679 | "inpaint_v25.fooocus.patch", 680 | "inpaint_v26.fooocus.patch", 681 | "MAT_Places512_G_fp16.safetensors", 682 | "Places_512_FullData_G.pth", 683 | "powerpaint_brushnet_text_encoder_fp16.safetensors", 684 | "powerpaint_v2.1.safetensors", 685 | "powerpaint_v2.1_pytorch_model.bin", 686 | "powerpaint_v2_brushnet_fp16.safetensors", 687 | "random_mask_brushnet_ckpt_sdxl_v0.safetensors", 688 | "segmentation_mask_brushnet_sdxl_v1.safetensors" 689 | ] 690 | } 691 | --------------------------------------------------------------------------------