From 22bde7957e18e8f9c4fb206227a6117dae391417 Mon Sep 17 00:00:00 2001 From: Tomoaki Hayasaka Date: Mon, 17 Apr 2023 01:58:33 +0900 Subject: [PATCH 1/7] Fix "Ctrl+Enter doesn't work when textarea has focus" regression introduced in #491. --- web/extensions/core/keybinds.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/web/extensions/core/keybinds.js b/web/extensions/core/keybinds.js index 1825007a6..42c228017 100644 --- a/web/extensions/core/keybinds.js +++ b/web/extensions/core/keybinds.js @@ -5,12 +5,6 @@ app.registerExtension({ name: id, init() { const keybindListener = function(event) { - const target = event.composedPath()[0]; - - if (target.tagName === "INPUT" || target.tagName === "TEXTAREA") { - return; - } - const modifierPressed = event.ctrlKey || event.metaKey; // Queue prompt using ctrl or command + enter @@ -19,6 +13,12 @@ app.registerExtension({ return; } + const target = event.composedPath()[0]; + + if (target.tagName === "INPUT" || target.tagName === "TEXTAREA") { + return; + } + const modifierKeyIdMap = { "s": "#comfy-save-button", 83: "#comfy-save-button", From 0ab5c619eafa026d4be1a3f6bf462a6f7f9d25d6 Mon Sep 17 00:00:00 2001 From: comfyanonymous Date: Mon, 17 Apr 2023 01:04:54 -0400 Subject: [PATCH 2/7] Clarify in README that it's AMD GPUs. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f610f9497..be2cb8ec5 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ Put your VAE in: models/vae At the time of writing this pytorch has issues with python versions higher than 3.10 so make sure your python/pip versions are 3.10. -### AMD (Linux only) +### AMD GPUs (Linux only) AMD users can install rocm and pytorch with pip if you don't have it already installed, this is the command to install the stable version: ```pip install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/rocm5.4.2``` From 884ea653c8d6fe19b3724f45a04a0d74cd881f2f Mon Sep 17 00:00:00 2001 From: comfyanonymous Date: Mon, 17 Apr 2023 11:05:15 -0400 Subject: [PATCH 3/7] Add a way for nodes to set a custom CFG function. --- comfy/samplers.py | 5 ++++- comfy/sd.py | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/comfy/samplers.py b/comfy/samplers.py index ed36442a9..05af6fe88 100644 --- a/comfy/samplers.py +++ b/comfy/samplers.py @@ -211,7 +211,10 @@ def sampling_function(model_function, x, timestep, uncond, cond, cond_scale, con max_total_area = model_management.maximum_batch_area() cond, uncond = calc_cond_uncond_batch(model_function, cond, uncond, x, timestep, max_total_area, cond_concat, model_options) - return uncond + (cond - uncond) * cond_scale + if "sampler_cfg_function" in model_options: + return model_options["sampler_cfg_function"](cond, uncond, cond_scale) + else: + return uncond + (cond - uncond) * cond_scale class CompVisVDenoiser(k_diffusion_external.DiscreteVDDPMDenoiser): diff --git a/comfy/sd.py b/comfy/sd.py index 9c632e240..1d7774742 100644 --- a/comfy/sd.py +++ b/comfy/sd.py @@ -250,6 +250,9 @@ class ModelPatcher: def set_model_tomesd(self, ratio): self.model_options["transformer_options"]["tomesd"] = {"ratio": ratio} + def set_model_sampler_cfg_function(self, sampler_cfg_function): + self.model_options["sampler_cfg_function"] = sampler_cfg_function + def model_dtype(self): return self.model.diffusion_model.dtype From 6f7852bc47de2fa432672a1b93c1727c0824d78b Mon Sep 17 00:00:00 2001 From: comfyanonymous Date: Mon, 17 Apr 2023 17:24:58 -0400 Subject: [PATCH 4/7] Add a LatentFromBatch node to pick a single latent from a batch. Works before and after sampling. --- nodes.py | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/nodes.py b/nodes.py index c775da00c..c745ce280 100644 --- a/nodes.py +++ b/nodes.py @@ -510,6 +510,24 @@ class EmptyLatentImage: return ({"samples":latent}, ) +class LatentFromBatch: + @classmethod + def INPUT_TYPES(s): + return {"required": { "samples": ("LATENT",), + "batch_index": ("INT", {"default": 0, "min": 0, "max": 63}), + }} + RETURN_TYPES = ("LATENT",) + FUNCTION = "rotate" + + CATEGORY = "latent" + + def rotate(self, samples, batch_index): + s = samples.copy() + s_in = samples["samples"] + batch_index = min(s_in.shape[0] - 1, batch_index) + s["samples"] = s_in[batch_index:batch_index + 1].clone() + s["batch_index"] = batch_index + return (s,) class LatentUpscale: upscale_methods = ["nearest-exact", "bilinear", "area"] @@ -685,7 +703,13 @@ def common_ksampler(model, seed, steps, cfg, sampler_name, scheduler, positive, if disable_noise: noise = torch.zeros(latent_image.size(), dtype=latent_image.dtype, layout=latent_image.layout, device="cpu") else: - noise = torch.randn(latent_image.size(), dtype=latent_image.dtype, layout=latent_image.layout, generator=torch.manual_seed(seed), device="cpu") + batch_index = 0 + if "batch_index" in latent: + batch_index = latent["batch_index"] + + generator = torch.manual_seed(seed) + for i in range(batch_index + 1): + noise = torch.randn([1] + list(latent_image.size())[1:], dtype=latent_image.dtype, layout=latent_image.layout, generator=generator, device="cpu") if "noise_mask" in latent: noise_mask = latent['noise_mask'] @@ -1073,6 +1097,7 @@ NODE_CLASS_MAPPINGS = { "VAELoader": VAELoader, "EmptyLatentImage": EmptyLatentImage, "LatentUpscale": LatentUpscale, + "LatentFromBatch": LatentFromBatch, "SaveImage": SaveImage, "PreviewImage": PreviewImage, "LoadImage": LoadImage, From 7b5eb196dbf4248eb6c67af2843cacb28863ce2f Mon Sep 17 00:00:00 2001 From: EllangoK Date: Mon, 17 Apr 2023 17:29:22 -0400 Subject: [PATCH 5/7] allows control arrow to edit attention in textarea --- web/extensions/core/editAttention.js | 117 +++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 web/extensions/core/editAttention.js diff --git a/web/extensions/core/editAttention.js b/web/extensions/core/editAttention.js new file mode 100644 index 000000000..d943290ce --- /dev/null +++ b/web/extensions/core/editAttention.js @@ -0,0 +1,117 @@ +import { app } from "/scripts/app.js"; + +// Allows you to edit the attention weight by holding ctrl (or cmd) and using the up/down arrow keys + +const id = "Comfy.EditAttention"; +app.registerExtension({ +name:id, + init() { + function incrementWeight(weight, delta) { + const floatWeight = parseFloat(weight); + if (isNaN(floatWeight)) return weight; + const newWeight = floatWeight + delta; + if (newWeight < 0) return "0"; + return String(Number(newWeight.toFixed(10))); + } + + function findNearestEnclosure(text, cursorPos) { + let start = cursorPos, end = cursorPos; + let openCount = 0, closeCount = 0; + + // Find opening parenthesis before cursor + while (start >= 0) { + start--; + if (text[start] === "(" && openCount === closeCount) break; + if (text[start] === "(") openCount++; + if (text[start] === ")") closeCount++; + } + if (start < 0) return false; + + openCount = 0; + closeCount = 0; + + // Find closing parenthesis after cursor + while (end < text.length) { + if (text[end] === ")" && openCount === closeCount) break; + if (text[end] === "(") openCount++; + if (text[end] === ")") closeCount++; + end++; + } + if (end === text.length) return false; + + return { start: start + 1, end: end }; + } + + function addWeightToParentheses(text) { + const parenRegex = /^\((.*)\)$/; + const parenMatch = text.match(parenRegex); + + const floatRegex = /:([+-]?(\d*\.)?\d+([eE][+-]?\d+)?)/; + const floatMatch = text.match(floatRegex); + + if (parenMatch && !floatMatch) { + return `(${parenMatch[1]}:1.0)`; + } else { + return text; + } + }; + + function editAttention(event) { + const inputField = event.composedPath()[0]; + const delta = 0.1; + + if (inputField.tagName !== "TEXTAREA") return; + if (!(event.key === "ArrowUp" || event.key === "ArrowDown")) return; + if (!event.ctrlKey && !event.metaKey) return; + + event.preventDefault(); + + let start = inputField.selectionStart; + let end = inputField.selectionEnd; + let selectedText = inputField.value.substring(start, end); + + // If there is no selection, attempt to find the nearest enclosure + if (!selectedText) { + const nearestEnclosure = findNearestEnclosure(inputField.value, start); + if (nearestEnclosure) { + start = nearestEnclosure.start; + end = nearestEnclosure.end; + selectedText = inputField.value.substring(start, end); + } else { + return; + } + } + + // If the selection ends with a space, remove it + if (selectedText[selectedText.length - 1] === " ") { + selectedText = selectedText.substring(0, selectedText.length - 1); + end -= 1; + } + + // If there are parentheses left and right of the selection, select them + if (inputField.value[start - 1] === "(" && inputField.value[end] === ")") { + start -= 1; + end += 1; + selectedText = inputField.value.substring(start, end); + } + + // If the selection is not enclosed in parentheses, add them + if (selectedText[0] !== "(" || selectedText[selectedText.length - 1] !== ")") { + console.log("adding parentheses", inputField.value[start], inputField.value[end], selectedText); + selectedText = `(${selectedText})`; + } + + // If the selection does not have a weight, add a weight of 1.0 + selectedText = addWeightToParentheses(selectedText); + + // Increment the weight + const weightDelta = event.key === "ArrowUp" ? delta : -delta; + const updatedText = selectedText.replace(/(.*:)(\d+(\.\d+)?)(.*)/, (match, prefix, weight, _, suffix) => { + return prefix + incrementWeight(weight, weightDelta) + suffix; + }); + + inputField.setRangeText(updatedText, start, end, "select"); + } + window.addEventListener("keydown", editAttention); + }, +}); From f03dade5ab8f17a165d63efc205eb34a2330b7d8 Mon Sep 17 00:00:00 2001 From: comfyanonymous Date: Mon, 17 Apr 2023 18:19:57 -0400 Subject: [PATCH 6/7] Fix bug. --- nodes.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nodes.py b/nodes.py index c745ce280..06b69f453 100644 --- a/nodes.py +++ b/nodes.py @@ -708,8 +708,9 @@ def common_ksampler(model, seed, steps, cfg, sampler_name, scheduler, positive, batch_index = latent["batch_index"] generator = torch.manual_seed(seed) - for i in range(batch_index + 1): + for i in range(batch_index): noise = torch.randn([1] + list(latent_image.size())[1:], dtype=latent_image.dtype, layout=latent_image.layout, generator=generator, device="cpu") + noise = torch.randn(latent_image.size(), dtype=latent_image.dtype, layout=latent_image.layout, generator=generator, device="cpu") if "noise_mask" in latent: noise_mask = latent['noise_mask'] From b8c636b10d39e77742f3f435bf6b85c3aa806583 Mon Sep 17 00:00:00 2001 From: comfyanonymous Date: Mon, 17 Apr 2023 18:21:24 -0400 Subject: [PATCH 7/7] Lower how much CTRL+arrow key changes the number. --- web/extensions/core/editAttention.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/extensions/core/editAttention.js b/web/extensions/core/editAttention.js index d943290ce..fe395c3ca 100644 --- a/web/extensions/core/editAttention.js +++ b/web/extensions/core/editAttention.js @@ -58,7 +58,7 @@ name:id, function editAttention(event) { const inputField = event.composedPath()[0]; - const delta = 0.1; + const delta = 0.025; if (inputField.tagName !== "TEXTAREA") return; if (!(event.key === "ArrowUp" || event.key === "ArrowDown")) return;