diff --git a/README.md b/README.md index 8b598e3..de298c6 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,75 @@ # ComfyUI PromptForge -Prompt enhancement split into three focused nodes: model/config, prompt generation, and a standalone config finder. +One pack for three model families: **Krea 2**, **MOSS Image** and **MiniMax H3**, plus shared prompt, loader, +sampler and video tooling. Each family has its own workflows, presets and setup notes below; the shared node +reference follows them. -## Nodes +- **Krea 2** — realistic and anime text-to-image, identity edit, and face transfer. +- **MOSS Image** — text-to-image and reference edit, with optional DMD accelerators. +- **MiniMax H3** — text/reference to video with sound, action presets, endless multi-part generation. +- **Shared** — loader, sampler with model-aware presets, enhancer, action library, image/video loaders, savers. + +## Krea 2 + +Required extras: `comfyui-krea2edit`, `krea2-nag` (only for the transfer workflow) and the Krea 2 models you +own. The LoRA node starts with the identity LoRA selected in the edit workflow. + +### Workflows + +- `KREA2_T2I_v2.json` — text to image at 1.5 MP. Write in `prompt_text`, pick an action or switch `use_action` + off, then queue. +- `KREA2_EDIT_v8.json` — identity edit from `LoadImage`; the face comes from the picture. Write only the change + (pose, framing, expression) in `prompt_text`. +- `KREA TRANSFER.json` — face transfer between a source and a target picture. + +### Settings + +Use the `Krea` presets in the sampler list: FinePorn 8-10 steps, cfg 1, `euler`/`simple`; turbo checkpoints +8 steps. Krea edit keeps identity best at 8-10 steps because the identity LoRA does the work, not more steps. + +### Negative prompts at cfg 1 + +`use_negative` on the encode node is the NAG switch. Off: the negative is ignored and the run takes the fast +cfg-1 path. On: the sampler runs the uncond pass and applies normalized attention guidance at cfg 1, without +changing cfg. Krea edit no longer uses a separate NAG node; the encoder and sampler handle it. + +## MOSS Image + +Required extras: ComfyUI with MOSS support, `ComfyUI-MOSS-Toolkit`, `moss_image_edit_lora_rank_64_bf16.safetensors` +for edit, and the MOSS models. Edit always keeps one reference image and `denoise 1`: the reference is +conditioning, not an img2img latent. + +### Workflows + +- `MOSS_T2I_v2.json` — text to image at 1.5 MP. +- `MOSS_EDIT_v3.json` — reference edit; pick the picture in `LoadImage`, write the change. + +### Settings and DMD + +Use the `MOSS` presets, matched to the model. Editing keeps the face at `image_strength` 1.5-2.0 with 20 steps, +cfg 2, `euler`/`normal`. The DMD LoRAs (`lodestones-8-steps`, `sft-4-steps`) run at 8 steps, cfg 1, +`er_sde`/`simple`; on PrimeLust use 12 steps and cfg 1.5. A denoise below 0.8 tiles the edit result. + +## MiniMax H3 + +Required extras: the H3 model, its VAE and an audio VAE. The `PromptForge_H3_AllInOne_Modes_v5.json` workflow +covers all modes; switch them in the Modes node. + +### Action node and roles + +57 acts, each with an explicit who-is-where description and a recommended 9:16 or 16:9 frame. Every role has +its own picture picker (Woman, Man, Partner): choose the reference slot, the first frame, none and only a +description, or `disabled`. Subject 3 defaults to `disabled`; pictures left unassigned become anatomy/detail +references, not extra people. See the [H3 Action reference](#promptforge-h3-action-adult-presets). + +### Writer and API + +The writer settings (template, style, sampling, retries) are described under the +[Prompt Enhancer](#promptforge-enhancer-promptforge_promptenhancer). For headless use, +`PromptForge_H3_AllInOne_Modes_v5.api.json` is already in `/prompt` format; the normal workflow stores the +image picker as an extra leading widget, which order-based converters must skip. + +## Shared node reference ### PromptForge Model (`PROMPTFORGE_Model`) @@ -13,13 +80,6 @@ Owns model choice and runtime configuration; outputs a `MODEL` bundle and a `REP - **Quant fallback**: unsupported combinations warn and use a working option — q5/q6 on safetensors load as q8, q8/q4 on a single-file encoder load in fp16, q8 switches to q4 when free VRAM is insufficient, CPU-offloaded layers are allowed (slower) instead of raising, a missing GGUF quant falls through the available files, and an incompatible KV/flash/SWA choice is retried with the safe value. The report re-checks local availability at run time, so stale `[AVAILABLE]` widget labels still show `[LOCAL]`. -### Example image workflows - -The pack does not bundle workflows; they live in your ComfyUI `user/default/workflows` folder. - -- `KREA2_T2I_v2.json`, `KREA2_EDIT_v8.json` — Krea 2 text-to-image and identity edit, built on PromptForge Action and Enhancer. Edit needs `comfyui-krea2edit`, `krea2-nag` and the `krea2_identity_edit_v2` LoRA. -- `MOSS_T2I_v2.json`, `MOSS_EDIT_v3.json` — MOSS-Image text-to-image and image edit. Edit needs ComfyUI 0.43+, `ComfyUI-MOSS-Toolkit` and `moss_image_edit_lora_rank_64_bf16.safetensors`. The edit workflow uses `task = custom` with full-size reference on purpose: the native ComfyUI MOSS model needs a full-size reference. - ### PromptForge Action (`PROMPTFORGE_ImageAction`) A pose library for any image model: 90 named actions (portraits, SFW solo and couple poses, nude and @@ -156,6 +216,8 @@ The Choose image picker, and an **Images** tab in the left sidebar, share one li ### Endless Video (category `🔮 PromptForge/Endless Video`) Makes a long MiniMax H3 video part by part. One node does the generating (**PromptForge H3 Endless Video**); small nodes plug into it, and every one of them is optional except the Story. The generator saves nothing: it outputs `frames` (IMAGE), `audio`, `prompts` and `report`, so you save them with PromptForge Save Video (or anything else) afterwards. A ready all-in-one workflow (`PromptForge_H3_AllInOne_Modes_v5.json`) is provided separately, in your workflow folder: text to video, image to video, characters / references, voice, extending an existing video, first-pass preview and endless multi-part video in one graph; you switch modes by bypassing the optional nodes (its READ ME note lists which). (The old `H3 Endless` and `H3 Options` nodes are gone; workflows built with them need rewiring.) +### PromptForge H3 Action (adult presets) + | Node | What it holds | |---|---| | **H3 Modes** | What writes the prompt (`prompt_source`: my idea or action preset; the other node is ignored) and what kind of video: **T2V** (text only, empty latent), **I2V** (first frame encoded and anchored at frame 0), **L2V** (last frame anchored at the end), **FL2V** (both), **REF2V** (pictures, videos and sounds encoded as references). Also one clip or several parts, extend an existing video, second stage on/off. Its overview lists the files chosen in the Load nodes with a tick for each; rows the mode does not use are dimmed. Each mode also picks its prompt template (T2VA, I2VA, L2VA, FL2VA, Ref2VA). | diff --git a/pyproject.toml b/pyproject.toml index f1e118f..ba2ade9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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.16" +version = "1.28.17" license = { file = "LICENSE" } dependencies = [ "transformers", diff --git a/sampler.py b/sampler.py index 0e1c4f9..5caedd9 100644 --- a/sampler.py +++ b/sampler.py @@ -4,7 +4,7 @@ It runs ComfyUI's own `comfy.sample.sample`, so every sampler, scheduler, model the stock nodes. On top of that: * `scheduler = auto` picks the family's schedule (LTX Video's token-count dependent one, otherwise simple), -* sigmas can come from a SIGMAS input, a typed list, or the LTXV schedule with its parameters, +* sigmas can come from a SIGMAS input or a typed list, * `runs` samples several seeds in one go and returns them as one batch, * settings can be saved as named presets, and recommended settings come from the model's sidecar and family (see sampler_defaults.py). @@ -22,12 +22,11 @@ import comfy.samplers import comfy.utils import folder_paths import latent_preview -from comfy_extras import nodes_easycache, nodes_lt +from comfy_extras import nodes_easycache from . import families, sampler_defaults AUTO = "auto" -LTXV = "ltxv" PRESET_FIELDS = ("steps", "cfg", "sampler_name", "scheduler", "denoise", "runs", "step_cache", "cache_threshold") # step caching skips whole model calls, which only pays off (and only stays clean) with enough steps AUTO_CACHE_MIN_STEPS = 15 @@ -110,11 +109,6 @@ def parse_sigmas(text): return torch.tensor(values, dtype=torch.float32) -def trim_for_denoise(sigmas, steps): - """Keep the last `steps` intervals, which is what denoise < 1 means for a schedule computed at steps / denoise.""" - return sigmas[-(steps + 1):] - - class PROMPTFORGE_Sampler: @classmethod def INPUT_TYPES(cls): @@ -128,7 +122,7 @@ class PROMPTFORGE_Sampler: "steps": ("INT", {"default": 20, "min": 1, "max": 10000}), "cfg": ("FLOAT", {"default": 4.0, "min": 0.0, "max": 100.0, "step": 0.1, "round": 0.01}), "sampler_name": (comfy.samplers.KSampler.SAMPLERS, {"default": "euler"}), - "scheduler": ([AUTO, LTXV] + comfy.samplers.KSampler.SCHEDULERS, {"default": AUTO, "tooltip": "auto: the family's schedule (ltxv for LTX Video, otherwise simple). ltxv: LTX Video's token-count dependent schedule."}), + "scheduler": ([AUTO] + comfy.samplers.KSampler.SCHEDULERS, {"default": AUTO, "tooltip": "auto uses a simple schedule."}), "denoise": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}), "runs": ("INT", {"default": 1, "min": 1, "max": 64, "tooltip": "Samples this many seeds (seed, seed+1 ...) and returns them as one batch."}), }, @@ -143,9 +137,6 @@ class PROMPTFORGE_Sampler: "start_at_step": ("INT", {"default": 0, "min": 0, "max": 10000, "advanced": True}), "end_at_step": ("INT", {"default": 10000, "min": 0, "max": 10000, "advanced": True}), "add_noise": ("BOOLEAN", {"default": True, "advanced": True}), - "ltxv_max_shift": ("FLOAT", {"default": 2.05, "min": 0.0, "max": 100.0, "step": 0.01, "advanced": True}), - "ltxv_base_shift": ("FLOAT", {"default": 0.95, "min": 0.0, "max": 100.0, "step": 0.01, "advanced": True}), - "ltxv_terminal": ("FLOAT", {"default": 0.1, "min": 0.0, "max": 0.99, "step": 0.01, "advanced": True}), "preview": (["default", "taesd", "latent2rgb", "none"], {"default": "default", "tooltip": "Live preview while sampling. default follows ComfyUI's --preview-method; taesd uses the model's tiny decoder when it is in models/vae_approx (taeh3 for MiniMax H3) and latent2rgb otherwise."}), }, } @@ -164,21 +155,17 @@ class PROMPTFORGE_Sampler: family = families.family_from_config(type(config).__name__) if config is not None else None return family.key if family else "" - def _sigmas(self, model, sampler_name, scheduler, steps, denoise, latent_image, sigmas, custom_sigmas, shifts): - """The sigmas to sample with: an input, a typed list, the LTXV schedule, or the named scheduler's.""" + def _sigmas(self, model, sampler_name, scheduler, steps, denoise, sigmas, custom_sigmas): + """The sigmas to sample with: an input, a typed list, or the named scheduler's.""" if sigmas is not None: return sigmas if custom_sigmas.strip(): return parse_sigmas(custom_sigmas) - if scheduler == LTXV: - total = max(steps, int(steps / denoise)) if denoise > 0 else steps - schedule = nodes_lt.LTXVScheduler.execute(total, shifts[0], shifts[1], True, shifts[2], {"samples": latent_image}).args[0] - return trim_for_denoise(schedule, steps) return comfy.samplers.KSampler(model, steps=steps, device=model.load_device, sampler=sampler_name, scheduler=scheduler, denoise=denoise, model_options=model.model_options).sigmas def sample(self, model, positive, negative, latent_image, seed, steps, cfg, sampler_name, scheduler, denoise, runs, pipe=None, sigmas=None, custom_sigmas="", step_cache="off", cache_threshold=0.2, cache_start=0.15, cache_end=0.95, - 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"): + start_at_step=0, end_at_step=10000, add_noise=True, 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): # the negative is real: force the uncond pass and guide with NAG instead of changing cfg @@ -191,17 +178,18 @@ class PROMPTFORGE_Sampler: if step_cache != "off": model = CACHES[step_cache].execute(model, cache_threshold, cache_start, cache_end, False).args[0] if scheduler == AUTO: - scheduler = LTXV if family == "ltxv" else "simple" + scheduler = "simple" samples = comfy.sample.fix_empty_latent_channels(model, latent_image["samples"], latent_image.get("downscale_ratio_spacial"), latent_image.get("downscale_ratio_temporal")) - used = self._sigmas(model, sampler_name, scheduler, steps, denoise, samples, sigmas, custom_sigmas, (ltxv_max_shift, ltxv_base_shift, ltxv_terminal)) + used = self._sigmas(model, sampler_name, scheduler, steps, denoise, sigmas, custom_sigmas) if runs > 1 and not isinstance(samples, torch.Tensor): raise ValueError("[PromptForge] runs > 1 needs a plain latent; this model uses a combined audio+video latent.") device = comfy.model_management.get_torch_device() if device.type == "cuda": torch.cuda.reset_peak_memory_stats(device) - latent_preview.set_preview_method(None if preview == "default" else preview) + if preview != "default": + latent_preview.set_preview_method(preview) try: try: callback = latent_preview.prepare_callback(model, steps) @@ -221,7 +209,8 @@ class PROMPTFORGE_Sampler: last_step=end_at_step if end_at_step < steps else None, force_full_denoise=end_at_step >= steps, noise_mask=latent_image.get("noise_mask"), sigmas=used, callback=callback, disable_pbar=not progress, seed=current)) finally: - latent_preview.set_preview_method() + if preview != "default": + latent_preview.set_preview_method() merged = results[0] if runs == 1 else torch.cat(results) out = latent_image.copy()