diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index b20e98a9f229b..6e8825ca0cf65 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -1350,6 +1350,7 @@ ./services/web-apps/calibre-web.nix ./services/web-apps/castopod.nix ./services/web-apps/coder.nix + ./services/web-apps/comfyui.nix ./services/web-apps/changedetection-io.nix ./services/web-apps/chatgpt-retrieval-plugin.nix ./services/web-apps/cloudlog.nix diff --git a/nixos/modules/services/web-apps/comfyui.nix b/nixos/modules/services/web-apps/comfyui.nix new file mode 100644 index 0000000000000..e239d2b7aedda --- /dev/null +++ b/nixos/modules/services/web-apps/comfyui.nix @@ -0,0 +1,405 @@ +{ config, lib, options, pkgs, ...}: + +with lib; + +let + cfg = config.services.comfyui; + defaultUser = "comfyui"; + defaultGroup = defaultUser; + service-name = "comfyui"; + mkComfyUIPackage = cfg: cfg.package.override { + modelsPath = "${cfg.dataPath}/models"; + inputPath = "${cfg.dataPath}/input"; + outputPath = "${cfg.dataPath}/output"; + customNodes = cfg.customNodes; + models = cfg.models; + }; +in +{ + options = { + services.comfyui = { + enable = mkEnableOption + ("The most powerful and modular stable diffusion GUI with a graph/nodes interface."); + + dataPath = mkOption { + type = types.str; + default = "/var/lib/${service-name}"; + description = "Path to the folders which stores models, custom nodes, input and output files."; + }; + + # TODO: This should probably use the global system cudaSupport by default + # and then allow overrides as necessary. + cudaSupport = mkOption { + type = types.bool; + default = false; + description = "Whether or not to enable CUDA for NVidia GPU acceleration."; + defaultText = literalExpression "false"; + example = literalExpression "true"; + }; + + rocmSupport = mkOption { + type = types.bool; + default = false; + description = "Whether or not to enable ROCM for ATi GPU acceleration."; + defaultText = literalExpression "false"; + example = literalExpression "true"; + }; + + package = mkOption { + type = types.package; + default = ( + if config.cudaSupport + then pkgs.comfyui-cuda + else if config.rocmSupport + then pkgs.comfyui-rocm + else pkgs.comfyui-cpu + ); + defaultText = literalExpression "pkgs.comfyui"; + example = literalExpression "pkgs.comfyui-rocm"; + description = "ComfyUI base package to use."; + }; + + user = mkOption { + type = types.str; + default = defaultUser; + example = "yourUser"; + description = '' + The user to run ComfyUI as. + By default, a user named `${defaultUser}` will be created whose home + directory will contain input, output, custom nodes and models. + ''; + }; + + group = mkOption { + type = types.str; + default = defaultGroup; + example = "yourGroup"; + description = '' + The group to run ComfyUI as. + By default, a group named `${defaultUser}` will be created. + ''; + }; + + useCPU = mkOption { + type = types.bool; + default = false; + description = '' + Uses the CPU for everything. Very slow, but needed if there is no hardware acceleration. + ''; + }; + + port = mkOption { + type = types.port; + default = 8188; + description = "Set the listen port for the Web UI and API."; + }; + + customNodes = mkOption { + type = types.listOf types.package; + default = []; + description = "custom nodes to add to the ComfyUI setup. Expects a list of packages from pkgs.comfyui-custom-nodes"; + }; + + listen = mkOption { + type = types.str; + # Assume a higher security posture by default. + default = "127.0.0.1"; + description = "The net mask to listen to."; + example = "0.0.0.0"; + }; + + cors-origin-domain = mkOption { + type = types.str; + default = "disabled"; + description = ''The CORS domain to bless. Use "disabled" to disable. This must include a port''; + example = "foo.com:443"; + }; + + cuda-malloc = mkOption { + type = types.nullOr types.bool; + default = null; + description = '' + Force cuda-malloc. Leave null to default. + ''; + }; + + max-upload-size = mkOption { + type = types.nullOr types.int; + default = null; + description = '' + Max upload size. Leave null to default. + ''; + }; + + multi-user = mkOption { + type = types.bool; + default = false; + description = '' + Enable a simple multi-user mode. This is helpful for separating user + settings but offers nothing in terms of security. + ''; + }; + + cuda-device = mkOption { + type = types.nullOr types.str; + description = ''The CUDA device to use. Query for this using lspci or lshw. Leave as null to auto-detect.''; + default = null; + }; + + verbose = mkOption { + type = types.bool; + description = ''Use verbose logging.''; + default = false; + }; + + cross-attention = mkOption { + type = types.nullOr (types.enum [ + "pytorch" + "quad" + "split" + ]); + description = ''Indicate which cross attention comfyui should use.''; + default = null; + }; + + preview-method = mkOption { + type = types.nullOr (types.enum [ + "none" + "auto" + "latent2rgb" + "taesd" + ]); + description = ''How to preview images being generated.''; + default = null; + }; + + # Argument dump: + # usage: comfyui [-h] [--listen [IP]] [--port PORT] + # [--enable-cors-header [ORIGIN]] + # [--max-upload-size MAX_UPLOAD_SIZE] + # [--extra-model-paths-config PATH [PATH ...]] + # [--output-directory OUTPUT_DIRECTORY] + # [--temp-directory TEMP_DIRECTORY] + # [--input-directory INPUT_DIRECTORY] [--auto-launch] + # [--disable-auto-launch] [--cuda-device DEVICE_ID] + # [--cuda-malloc | --disable-cuda-malloc] + # [--dont-upcast-attention] [--force-fp32 | --force-fp16] + # [--bf16-unet | --fp16-unet | --fp8_e4m3fn-unet | --fp8_e5m2-unet] + # [--fp16-vae | --fp32-vae | --bf16-vae] [--cpu-vae] + # [--fp8_e4m3fn-text-enc | --fp8_e5m2-text-enc | --fp16-text-enc | --fp32-text-enc] + # [--directml [DIRECTML_DEVICE]] [--disable-ipex-optimize] + # [--preview-method [none,auto,latent2rgb,taesd]] + # [--use-split-cross-attention | --use-quad-cross-attention | --use-pytorch-cross-attention] + # [--disable-xformers] + # [--gpu-only | --highvram | --normalvram | --lowvram | --novram | --cpu] + # [--disable-smart-memory] [--deterministic] + # [--dont-print-server] [--quick-test-for-ci] + # [--windows-standalone-build] [--disable-metadata] + # [--multi-user] [--verbose] + extraArgs = mkOption { + type = types.attrsOf types.str; + default = {}; + defaultText = literalExpression '' + { + disable-xformers = true; + preview-method = "auto"; + verbose = true; + } + ''; + description = '' + Additional arguments to be passed to comfyui. + ''; + }; + + models = mkOption (let + src-option = mkOption { + type = types.attrsOf types.package; + default = {}; + description = '' + A key value pair of names to fetchers for ComfyUI models. + ''; + }; + in { + type = types.submodule { + options = { + checkpoints = src-option; + clip = src-option; + clip_vision = src-option; + configs = src-option; + controlnet = src-option; + embeddings = src-option; + loras = src-option; + upscale_models = src-option; + vae = src-option; + vae_approx = src-option; + }; + }; + default = { + checkpoints = {}; + clip = {}; + clip_vision = {}; + configs = {}; + controlnet = {}; + embeddings = {}; + loras = {}; + upscale_models = {}; + vae = {}; + vae_approx = {}; + }; + description = '' + Models for ComfyUI to use. These are categorized by the model type + (checkpoints, loras, vae, etc.). Each of those are an attrset of + names of the model (like `juggernaught-xl`) and a fetcher indicating + the URL, type, and SHA of the model. + ''; + }); + }; + }; + + config = mkIf cfg.enable { + users.users = mkIf (cfg.user == defaultUser) { + ${defaultUser} = + { group = cfg.group; + home = cfg.dataPath; + createHome = true; + description = "ComfyUI daemon user"; + isSystemUser = true; + }; + }; + + users.groups = mkIf (cfg.group == defaultGroup) { + ${defaultGroup} = {}; + }; + + systemd.services.comfyui = let + package = mkComfyUIPackage cfg; + in { + description = "ComfyUI Service"; + wantedBy = [ "multi-user.target" ]; + environment = { + DATA = cfg.dataPath; + }; + + preStart = let + inherit (lib.trivial) throwIfNot; + inherit (lib) isAttr isString; + inherit (lib.strings) concatStrings intersperse; + inherit (lib.lists) flatten; + inherit (lib.attrsets) attrValues mapAttrsToList; + join = (sep: (xs: concatStrings (intersperse sep xs))); + join-lines = join "\n"; + src-to-symlink = path: name: ( + throwIfNot (isString path) "path must be a string." + throwIfNot (isString name) "name must be a string." + lib.traceVal '' + ln -snf ${path}/${name} $out + '' + ); + + # Take all of the model files for the various model types defined in the + # config of `models`, and translate it into a series of symlink shell + # invocations. The destination corresponds to the definitions in + # `config-data`. + # ex: + # + # linkModels + # "/var/lib/comfyui/models" + # { + # checkpoints = { + # "sdxxl.safetensors" = pkgs.fetchurl { + # url = "foo.com/sdxxl-v123"; + # sha256 = "sha-string"; + # }; + # }; + # upscale_modules = { + # "controlnet.pth" = { + # fancy-pth = pkgs.fetchurl { + # url = "foo.com/fancy-pth-v456"; + # sha256 = "sha-string"; + # }; + # }; + # }; + # } + # Returns: '' + # ln -snf /var/lib/comfyui/models/checkpoints + # ln -snf /var/lib/comfyui/models/upscale_modules + # '' + # + linkModels = base-path: models: + throwIfNot (isString base-path) "base-path must be a string." + throwIfNot (isAttrs models) "models must be an attrset." + (join-lines (builtins.map + (x: '' + ln -snf ${x.drv} ${cfg.dataPath}/models/${x.model-type} + '') + (flatten + (mapAttrsToList + (type: models-by-name: { + model-type = type; + drv = pkgs.linkFarm "comfyui-models-${type}" ( + mapAttrsToList (name: path: { + inherit name path; + }) models-by-name + ); + }) + models + ) + ) + )) + ; + in '' + mkdir -p ${cfg.dataPath}/input + mkdir -p ${cfg.dataPath}/output + ln -snf ${package}/custom_nodes ${cfg.dataPath}/custom_nodes + ln -snf ${package}/extra_model_paths.yaml ${cfg.dataPath}/extra_model_paths.yaml + mkdir -p ${cfg.dataPath}/models + ${linkModels "${cfg.dataPath}/models" cfg.models} + ''; + + serviceConfig = { + User = cfg.user; + Group = cfg.group; + # These directories must be relative to /var/lib. Absolute paths are + # will error with: + # : StateDirectory= path is absolute, ignoring: + RuntimeDirectory = [ service-name ]; + StateDirectory = [ service-name ]; + WorkingDirectory = "/run/${service-name}"; + ExecStart = let + args = cli.toGNUCommandLine {} ({ + cpu = cfg.useCPU; + enable-cors-header = cfg.cors-origin-domain; + cuda-device = cfg.cuda-device; + listen = cfg.listen; + max-upload-size = cfg.max-upload-size; + multi-user = cfg.multi-user; + port = cfg.port; + verbose = cfg.verbose; + } + // ( + if cfg.cuda-malloc != null + then ( + if cfg.cuda-malloc + then { cuda-malloc = true; } + else { disable-cuda-malloc = true; } + ) + else {} + ) + // (lib.optionalAttrs (cfg.cross-attention != null) { + "use-${cfg.cross-attention}-cross-attention" = true; + }) + // cfg.extraArgs); + in ''${package}/bin/comfyui ${toString args}''; + # comfyui is prone to crashing on long slow workloads. + Restart = "always"; + StartLimitBurst = 3; + }; + unitConfig = { + # Prevent it from restarting _too_ much though. Stop if three times a + # minute. This might need a better default from someone with better + # sysadmin chops. + StartLimitIntervalSec = "1m"; + }; + }; + }; +} diff --git a/pkgs/by-name/co/comfyui/comfyui-custom-scripts-remove-js-install-step.patch b/pkgs/by-name/co/comfyui/comfyui-custom-scripts-remove-js-install-step.patch new file mode 100644 index 0000000000000..805a07cb1f267 --- /dev/null +++ b/pkgs/by-name/co/comfyui/comfyui-custom-scripts-remove-js-install-step.patch @@ -0,0 +1,70 @@ +diff --git a/__init__.py b/__init__.py +index ae43dd2..07435f7 100644 +--- a/__init__.py ++++ b/__init__.py +@@ -21,5 +21,5 @@ if init(): + if hasattr(module, "NODE_DISPLAY_NAME_MAPPINGS") and getattr(module, "NODE_DISPLAY_NAME_MAPPINGS") is not None: + NODE_DISPLAY_NAME_MAPPINGS.update(module.NODE_DISPLAY_NAME_MAPPINGS) + +-WEB_DIRECTORY = "./web" ++WEB_DIRECTORY = "./web/js" + __all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS", "WEB_DIRECTORY"] +diff --git a/pysssss.py b/pysssss.py +index b1024f5..4f3b122 100644 +--- a/pysssss.py ++++ b/pysssss.py +@@ -117,46 +117,6 @@ def is_junction(path): + except OSError: + return False + +- +-def install_js(): +- src_dir = get_ext_dir("web/js") +- if not os.path.exists(src_dir): +- log("No JS") +- return +- +- should_install = should_install_js() +- if should_install: +- log("it looks like you're running an old version of ComfyUI that requires manual setup of web files, it is recommended you update your installation.", "warning", True) +- dst_dir = get_web_ext_dir() +- linked = os.path.islink(dst_dir) or is_junction(dst_dir) +- if linked or os.path.exists(dst_dir): +- if linked: +- if should_install: +- log("JS already linked") +- else: +- os.unlink(dst_dir) +- log("JS unlinked, PromptServer will serve extension") +- elif not should_install: +- shutil.rmtree(dst_dir) +- log("JS deleted, PromptServer will serve extension") +- return +- +- if not should_install: +- log("JS skipped, PromptServer will serve extension") +- return +- +- if link_js(src_dir, dst_dir): +- log("JS linked") +- return +- +- log("Copying JS files") +- shutil.copytree(src_dir, dst_dir, dirs_exist_ok=True) +- +- +-def should_install_js(): +- return not hasattr(PromptServer.instance, "supports") or "custom_nodes_from_web" not in PromptServer.instance.supports +- +- + def init(check_imports=None): + log("Init") + +@@ -169,7 +129,6 @@ def init(check_imports=None): + type="ERROR", always=True) + return False + +- install_js() + return True + + diff --git a/pkgs/by-name/co/comfyui/custom-nodes.nix b/pkgs/by-name/co/comfyui/custom-nodes.nix index 938fe996997b2..b46139b2742b0 100644 --- a/pkgs/by-name/co/comfyui/custom-nodes.nix +++ b/pkgs/by-name/co/comfyui/custom-nodes.nix @@ -1,15 +1,36 @@ -{ - fetchFromGitHub -, stdenv +# TODO: Document how to override this data (let alone that it exists). Show how +# to specify the workflow directory ala: +# https://github.com/pythongosssss/ComfyUI-Custom-Scripts/blob/main/pysssss.example.json +{ comfyui-custom-scripts-autocomplete-text ? (builtins.fetchurl { + url = "https://gist.githubusercontent.com/pythongosssss/1d3efa6050356a08cea975183088159a/raw/a18fb2f94f9156cf4476b0c24a09544d6c0baec6/danbooru-tags.txt"; + sha256 = "15xmm538v0mjshncglpbkw2xdl4cs6y0faz94vfba70qq87plz4p"; +}) +, comfyui-custom-scripts-data ? { + name = "CustomScripts"; + logging = false; +} +, fetchFromGitHub , lib +, writeTextFile +, pkgs +, stdenv }: let + # Patches don't apply to $src, and as with many scripting languages that don't + # have a build output per se, we just want the script source itself placed + # into $out. So just copy everything into $out instead of from $src so we can + # make sure we get everything in the future, and we use the patched versions. + install = '' + shopt -s dotglob + shopt -s extglob + cp -r ./!($out|$src) $out/ +''; mkComfyUICustomNodes = args: stdenv.mkDerivation ({ installPhase = '' runHook preInstall mkdir -p $out/ - cp -r $src/* $out/ + ${install} runHook postInstall ''; @@ -17,16 +38,288 @@ let } // args); in { + inherit mkComfyUICustomNodes; + + # Generates masks for inpainting based on text prompts.. + # https://github.com/biegert/ComfyUI-CLIPSeg + clipseg = mkComfyUICustomNodes { + pname = "clipseg"; + version = "unstable-2023-04-12"; + pyproject = true; + installPhase = '' + runHook preInstall + mkdir -p $out + cp $src/custom_nodes/clipseg.py $out/__init__.py # https://github.com/biegert/ComfyUI-CLIPSeg/issues/12 + runHook postInstall + ''; + src = fetchFromGitHub { + owner = "biegert"; + repo = "ComfyUI-CLIPSeg"; + rev = "7f38951269888407de45fb934958c30c27704fdb"; + hash = "sha256-qqrl1u1wOKMRRBvMHD9gE80reDqLWn+vJEiM1yKZeUo="; + fetchSubmodules = true; + }; + }; + + # https://github.com/Fannovel16/comfyui_controlnet_aux + # Nodes for providing ControlNet hint images. + controlnet-aux = mkComfyUICustomNodes { + pname = "comfyui-controlnet-aux"; + version = "unstable-2024-04-05"; + pyproject = true; + dependencies = with pkgs.python3Packages; [ + matplotlib + opencv4 + scikit-image + ]; + src = fetchFromGitHub { + owner = "Fannovel16"; + repo = "comfyui_controlnet_aux"; + rev = "c0b33402d9cfdc01c4e0984c26e5aadfae948e05"; + hash = "sha256-D9nzyE+lr6EJ+9Egabu+th++g9ZR05wTg0KSRUBaAZE="; + fetchSubmodules = true; + }; + }; + + # Manages workflows in comfyui such that they can be version controlled + # easily. + # https://github.com/talesofai/comfyui-browser + # + # This uses a fork that allows for configurable directories. + comfyui-browser = mkComfyUICustomNodes { + pname = "comfyui-browser"; + version = "unstable-fork-2024-04-21"; + src = fetchFromGitHub { + owner = "LoganBarnett"; + repo = "comfyui-browser"; + rev = "209106316655a58b7f49695c0a0bcab57d5a0c0e"; + hash = "sha256-/vYCxzT0YvBiNl3B0s8na5QRYWxUgNUleRgCQrEJgvI="; + }; + installPhase = '' + mkdir -p $out/ + ${install} + cp ${pkgs.writeText "config.json" (builtins.toJSON { + collections = "/var/lib/comfyui/comfyui-browser-collections"; + download_logs = "/var/lib/comfyui/comfyui-browser-download-logs"; + outputs = "/var/lib/comfyui/output"; + sources = "/var/lib/comfyui/comfyui-browser-sources"; + })} $out/config.json + ''; + }; + + # Show the time spent in various nodes of a workflow. + comfyui-profiler = mkComfyUICustomNodes { + pname = "comfyui-profiler"; + version = "unstable-2024-01-11"; + src = fetchFromGitHub { + owner = "tzwm"; + repo = "comfyui-profiler"; + rev = "942dfe484c481f7cdab8806fa278b3df371711bf"; + hash = "sha256-J0iTRycneGYF6RGJyZ/mVtEge1dxakwny0yfG1ceOd8="; + }; + }; + + # https://github.com/crystian/ComfyUI-Crystools + # Various tools/nodes: + # 1. Resources monitor (CUDA GPU usage, CPU usage, memory, etc). + # a. CUDA only. + # 2. Progress monitor. + # 3. Compare images. + # 4. Compare workflow JSON documents. + # 5. Debug values. + # 6. Pipes - A means of condensing multiple inputs or outputs together into a + # single output or input (respectively). + # 7. Better nodes for: + # a. Saving images. + # b. Loading images. + # c. See "hidden" data(?). + # 8. New primitives (list), and possibly better/different replacements for + # original primitives. + # 9. Switch - turn on or off functionality based on a boolean primitive. + # 9. More™! + # + comfyui-crystools = mkComfyUICustomNodes (let + version = "1.12.0"; + in { + pname = "comfyui-cystools"; + inherit version; + src = fetchFromGitHub { + owner = "crystian"; + repo = "ComfyUI-Crystools"; + rev = version; + hash = "sha256-ZzbMgFeV5rrRU76r/wKnbhujoqE7UDOSaLgQZhguXuY="; + }; + passthru.dependencies = with pkgs.python3Packages; [ + deepdiff + py-cpuinfo + pynvml + ]; + }); + + # https://github.com/pythongosssss/ComfyUI-Custom-Scripts + # Various tools/nodes: + # 1. Autocomplete of keywords, showing keyword count in the model. + # 2. Auto-arrange graph. + # 3. Always snap to grid. + # 4. Loaders that show preview images, have example prompts, and are cataloged + # under folders. + # 5. Checkpoint/LoRA metadata viewing. + # 6. Image constraints (I assume for preview). + # 7. Favicon for comfyui. + # 8. Image feed showing images of the current session. + # 9. Advanced KSampler denoise "helper" - asks for steps? + # 10. Lock nodes and groups (groups doesn't have this in stock comfyui) to + # prevent moving. + # 11. Math/eval expressions as a node. + # 12. Node finder. + # 13. Preset text - save and reuse text. + # 14. Play sound node - great for notification of completion! + # 15. Repeaters. + # 16. Show text (can be good for loading images and getting the prompt text + # out). + # 17. Show image on menu. + # 18. String (replace) function - Substitution via regular expression or exact + # match. + # 19. Save and load workflows (already in stock?). + # 20. 90º reroutes...? + # 21. Link render mode - linear, spline, straight. + # + comfyui-custom-scripts = mkComfyUICustomNodes (let + pysssss-config = (writeTextFile { + name = "pysssss.json"; + text = (lib.generators.toJSON {} comfyui-custom-scripts-data); + }); + in { + pname = "comfyui-custom-scripts"; + version = "unstable-2024-04-07"; + src = fetchFromGitHub { + owner = "pythongosssss"; + repo = "ComfyUI-Custom-Scripts"; + rev = "3f2c021e50be2fed3c9d1552ee8dcaae06ad1fe5"; + hash = "sha256-Kc0zqPyZESdIQ+umepLkQ/GyUq6fev0c/Z7yyTew5dY="; + }; + installPhase = '' + runHook preInstall + mkdir -p $out/ + cp -r $src/* $out/ + cp ${pysssss-config} $out/pysssss.json + mkdir -p $out/user + chmod +w $out/user + cp ${comfyui-custom-scripts-autocomplete-text} $out/user/autocomplete.txt + chmod -w $out/user + # Copy the patched version separately. See + # https://discourse.nixos.org/t/solved-how-to-apply-a-patch-in-a-flake/27227/4 + # for reference. Perhaps a better reference exists? + # But this doesn't work for reasons I can't understand. I get permission + # denied. + # cp pysssss.py $out/ + # It seems that I need to grant myself write permissions first. Is any of + # this documented anywhere? + chmod -R +w $out + cp pysssss.py $out/ + cp __init__.py $out/ + # Put it back I guess? + chmod -R -w $out/ + runHook postInstall + ''; + patches = [ + ./comfyui-custom-scripts-remove-js-install-step.patch + ]; + }); + + # https://github.com/LEv145/images-grid-comfy-plugin + images-grid-comfy-plugin = mkComfyUICustomNodes (let + version = "2.6"; + in { + pname = "images-grid-comfy-plugin"; + inherit version; + src = fetchFromGitHub { + owner = "LEv145"; + repo = "images-grid-comfy-plugin"; + # Space character is deliberate. + rev = "refs/tags/${version}"; + hash = "sha256-YG08pF6Z44y/gcS9MrCD/X6KqG99ig+VKLfZOd49w9s="; + }; + }); + + # https://github.com/Acly/comfyui-inpaint-nodes + # Provides nodes for doing better inpainting. + inpaint-nodes = mkComfyUICustomNodes { + pname = "comfyui-inpaint-nodes"; + version = "unstable-2024-04-08"; + pyproject = true; + src = fetchFromGitHub { + owner = "Acly"; + repo = "comfyui-inpaint-nodes"; + rev = "8469f5531116475abb6d7e9c04720d0a29485a66"; + hash = "sha256-Ane8zA9BN9QlRcQOwji4hZF2xoDPe/GvSqEyAPR+T28="; + fetchSubmodules = true; + }; + }; + + # https://github.com/cubiq/ComfyUI_IPAdapter_plus + # This allows use of IP-Adapter models (IP meaning Image Prompt in this + # context). IP-Adapter models can out-perform fine tuned models + # (checkpoints?). + ipadapter-plus = mkComfyUICustomNodes { + pname = "comfyui-ipadapter-plus"; + version = "unstable-2024-04-10"; + pyproject = true; + src = fetchFromGitHub { + owner = "cubiq"; + repo = "ComfyUI_IPAdapter_plus"; + rev = "417d806e7a2153c98613e86407c1941b2b348e88"; + hash = "sha256-yuZWc2PsgMRCFSLTqniZDqZxevNt2/na7agKm7Xhy7Y="; + fetchSubmodules = true; + }; + }; + + # https://github.com/Gourieff/comfyui-reactor-node + # Fast and simple face swap node(s). + reactor-node = mkComfyUICustomNodes { + pname = "comfyui-reactor-node"; + version = "unstable-2024-04-07"; + pyproject = true; + dependencies = with pkgs.python3Packages; [ insightface ]; + src = fetchFromGitHub { + owner = "Gourieff"; + repo = "comfyui-reactor-node"; + rev = "05bf228e623c8d7aa5a33d3a6f3103a990cfe09d"; + hash = "sha256-2IrpOp7N2GR1zA4jgMewAp3PwTLLZa1r8D+/uxI8yzw="; + fetchSubmodules = true; + }; + }; + + # https://github.com/Acly/comfyui-tooling-nodes + # Make ComfyUI more friendly towards API usage. + tooling-nodes = mkComfyUICustomNodes { + pname = "comfyui-tooling-nodes"; + version = "unstable-2024-03-04"; + pyproject = true; + src = fetchFromGitHub { + owner = "Acly"; + repo = "comfyui-tooling-nodes"; + rev = "bcb591c7b998e13f12e2d47ee08cf8af8f791e50"; + hash = "sha256-dXeDABzu0bhMDN/ryHac78oTyEBCmM/rxCIPfr99ol0="; + fetchSubmodules = true; + }; + }; + + # Handle upscaling of smaller images into larger ones. This is helpful to go + # from a prototyped image to a highly detailed, high resolution version. ultimate-sd-upscale = mkComfyUICustomNodes { pname = "ultimate-sd-upscale"; - version = "unstable-2023-08-16"; - + version = "unstable-2024-03-30"; src = fetchFromGitHub { owner = "ssitu"; repo = "ComfyUI_UltimateSDUpscale"; - rev = "6ea48202a76ccf5904ddfa85f826efa80dd50520"; - hash = "sha256-fUZ0AO+LARa+x30Iu+8jvEDun4T3f9G0DOlB2XNxY9Q="; + rev = "b303386bd363df16ad6706a13b3b47a1c2a1ea49"; + hash = "sha256-kcvhafXzwZ817y+8LKzOkGR3Y3QBB7Nupefya6s/HF4="; fetchSubmodules = true; }; }; + + # More to add: + # https://github.com/pythongosssss/ComfyUI-WD14-Tagger - Reverse image + # inference - generate keywords (or a prompt of sorts) from an image. } diff --git a/pkgs/by-name/co/comfyui/krita-ai-custom-nodes.nix b/pkgs/by-name/co/comfyui/krita-ai-custom-nodes.nix new file mode 100644 index 0000000000000..18b8b23b101f5 --- /dev/null +++ b/pkgs/by-name/co/comfyui/krita-ai-custom-nodes.nix @@ -0,0 +1,13 @@ +## +# Krita.ai can use ComfyUI as a backend, but there are some required +# custom-nodes that must be configured beforehand. See +# https://github.com/Acly/krita-ai-diffusion/wiki/ComfyUI-Setup for Krita's +# documentation on the topic. +{ custom-nodes }: with custom-nodes; [ + controlnet-aux + inpaint-nodes + ipadapter-plus + reactor-node + tooling-nodes + ultimate-sd-upscale +]