Use NAG for negative prompts at cfg 1
This commit is contained in:
@@ -140,10 +140,10 @@ A sound, or a video (as 24 fps frames with its sound), from the input folder wit
|
||||
Loads an image from the input folder through a thumbnail browser (search, folder filter, upload). Each image can have a stored description (`user/promptforge/image_descriptions.json`): *Save description* stores the text typed in the node (picking another image never changes that box; the stored text shows as its placeholder), and the node outputs it as `DESCRIPTION` next to `IMAGE`, `MASK` and `NAME`. Text typed in the node is used as is; an empty box falls back to the stored description. Handy for switching a character and its description together in reference-to-video workflows.
|
||||
|
||||
### PromptForge Text Encode (`PROMPTFORGE_TextEncode`)
|
||||
The positive prompt and the negative prompt in one node: `text` becomes POSITIVE and `negative` becomes NEGATIVE on the same CLIP, so a graph needs one encode instead of two. `use_negative` (off by default) decides whether the negative is real; off returns the positive for both outputs and keeps a cfg-1 run fast, on lets the sampler raise cfg 1 to 1.5 for the negative pass.
|
||||
The positive prompt and the negative prompt in one node: `text` becomes POSITIVE and `negative` becomes NEGATIVE on the same CLIP, so a graph needs one encode instead of two. `use_negative` (off by default) decides whether the negative is real; off returns the positive for both outputs and keeps a cfg-1 run fast, on runs the negative through normalized attention guidance at cfg 1 without touching cfg.
|
||||
|
||||
### PromptForge MOSS Edit Encode / Krea Edit Encode (`PROMPTFORGE_MossEditEncode`, `PROMPTFORGE_KreaEditEncode`)
|
||||
Edit conditioning for both CFG passes in one node. The MOSS node attaches the reference images and VAE to the positive and to the negative, each with its own image strength. The Krea node grounds the positive and the negative on the source image through the `Krea2EditGroundedEncode` path. The prompt is the positive instruction, the negative is what to avoid. `use_negative` (off by default) enables the negative: off returns the positive for both outputs and keeps the fast cfg-1 path; on makes the sampler raise a cfg of 1 to 1.5 so the negative pass runs. Leave it off on turbo/distilled models unless the negative matters, then raise cfg for more effect.
|
||||
Edit conditioning for both CFG passes in one node. The MOSS node attaches the reference images and VAE to the positive and to the negative, each with its own image strength. The Krea node grounds the positive and the negative on the source image through the `Krea2EditGroundedEncode` path. The prompt is the positive instruction, the negative is what to avoid. `use_negative` (off by default) enables the negative: off returns the positive for both outputs and keeps the fast cfg-1 path; on makes the sampler run the uncond pass and apply normalized attention guidance, so the negative works **at cfg 1 without changing cfg**. Leave it off on turbo models unless the negative matters.
|
||||
|
||||
#### Image library (picker and sidebar)
|
||||
The Choose image picker, and an **Images** tab in the left sidebar, share one library of the input folder. **Tools** runs a local vision model (a GGUF with its mmproj, found in your text_encoders / LLM folders) that gives every image keywords, a caption, a people count and an NSFW flag; results are stored in `user/promptforge/image_tags.json`.
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
[project]
|
||||
name = "ComfyUI-PromptForge"
|
||||
description = "ComfyUI prompt enhancement suite: model/config node with GPU-aware auto settings, prompt enhancer with a preset catalog, and a standalone config finder."
|
||||
version = "1.28.14"
|
||||
version = "1.28.15"
|
||||
license = { file = "LICENSE" }
|
||||
dependencies = [
|
||||
"transformers",
|
||||
|
||||
+21
-1
@@ -78,6 +78,22 @@ async def _recommend_route(request):
|
||||
return web.json_response({"settings": settings, "source": source})
|
||||
|
||||
|
||||
def _nag_pre_cfg(args):
|
||||
"""Normalized attention guidance on the conditioning batch, so a negative works at cfg 1."""
|
||||
conds = args["conds_out"]
|
||||
if len(conds) < 2 or conds[1] is None:
|
||||
return conds
|
||||
positive, negative = conds[0], conds[1]
|
||||
scale, alpha, tau = 4.0, 0.25, 2.5
|
||||
guided = positive * scale - negative * (scale - 1.0)
|
||||
norm_positive = torch.norm(positive, p=1, dim=-1, keepdim=True).clamp_min(1e-6)
|
||||
norm_guided = torch.norm(guided, p=1, dim=-1, keepdim=True).clamp_min(1e-6)
|
||||
ratio = norm_guided / norm_positive
|
||||
guided = guided * (torch.minimum(ratio, torch.full_like(ratio, tau)) / ratio)
|
||||
conds[0] = guided * alpha + positive * (1.0 - alpha)
|
||||
return conds
|
||||
|
||||
|
||||
def parse_sigmas(text):
|
||||
"""'1, 0.8, 0.5' -> descending sigma tensor ending in 0."""
|
||||
values = [float(part) for part in text.replace("\n", ",").split(",") if part.strip()]
|
||||
@@ -159,7 +175,11 @@ class PROMPTFORGE_Sampler:
|
||||
start_at_step=0, end_at_step=10000, add_noise=True, ltxv_max_shift=2.05, ltxv_base_shift=0.95, ltxv_terminal=0.1, preview="default"):
|
||||
family = self._family(model, pipe)
|
||||
if cfg <= 1.0 and any(len(cond) > 1 and cond[1].get("promptforge_use_negative") for cond in negative):
|
||||
cfg = 1.5
|
||||
# the negative is real: force the uncond pass and guide with NAG instead of changing cfg
|
||||
model = model.clone()
|
||||
model.model_options = dict(model.model_options)
|
||||
model.model_options["disable_cfg1_optimization"] = True
|
||||
model.model_options["sampler_pre_cfg_function"] = [_nag_pre_cfg] + list(model.model_options.get("sampler_pre_cfg_function", []))
|
||||
if step_cache == "auto":
|
||||
step_cache = "easycache" if steps >= AUTO_CACHE_MIN_STEPS else "off"
|
||||
if step_cache != "off":
|
||||
|
||||
Reference in New Issue
Block a user