diff --git a/examples/z-image_simple_prompt_batcher.json b/examples/z-image_simple_prompt_batcher.json new file mode 100644 index 0000000..d6fa6dc --- /dev/null +++ b/examples/z-image_simple_prompt_batcher.json @@ -0,0 +1,769 @@ +{ + "id": "9ae6082b-c7f4-433c-9971-7a8f65a3ea65", + "revision": 0, + "last_node_id": 59, + "last_link_id": 58, + "nodes": [ + { + "id": 40, + "type": "VAELoader", + "pos": [ + 111.43181046036977, + 545.6843412137608 + ], + "size": [ + 270, + 58 + ], + "flags": {}, + "order": 0, + "mode": 0, + "inputs": [], + "outputs": [ + { + "name": "VAE", + "type": "VAE", + "links": [ + 39 + ] + } + ], + "properties": { + "cnr_id": "comfy-core", + "ver": "0.3.73", + "Node name for S&R": "VAELoader", + "models": [ + { + "name": "ae.safetensors", + "url": "https://huggingface.co/Comfy-Org/z_image_turbo/resolve/main/split_files/vae/ae.safetensors", + "directory": "vae" + } + ], + "enableTabs": false, + "tabWidth": 65, + "tabXOffset": 10, + "hasSecondTab": false, + "secondTabText": "Send Back", + "secondTabOffset": 80, + "secondTabWidth": 65 + }, + "widgets_values": [ + "ae.safetensors" + ] + }, + { + "id": 46, + "type": "UNETLoader", + "pos": [ + 111.43181046036977, + 265.68434121376123 + ], + "size": [ + 270, + 82 + ], + "flags": {}, + "order": 1, + "mode": 0, + "inputs": [], + "outputs": [ + { + "name": "MODEL", + "type": "MODEL", + "links": [ + 37 + ] + } + ], + "properties": { + "cnr_id": "comfy-core", + "ver": "0.3.73", + "Node name for S&R": "UNETLoader", + "models": [ + { + "name": "z_image_turbo_bf16.safetensors", + "url": "https://huggingface.co/Comfy-Org/z_image_turbo/resolve/main/split_files/diffusion_models/z_image_turbo_bf16.safetensors", + "directory": "diffusion_models" + } + ], + "enableTabs": false, + "tabWidth": 65, + "tabXOffset": 10, + "hasSecondTab": false, + "secondTabText": "Send Back", + "secondTabOffset": 80, + "secondTabWidth": 65 + }, + "widgets_values": [ + "z_image_turbo_bf16.safetensors", + "default" + ] + }, + { + "id": 44, + "type": "KSampler", + "pos": [ + 795.0082216717279, + 377.10099778781864 + ], + "size": [ + 315, + 262 + ], + "flags": {}, + "order": 10, + "mode": 0, + "inputs": [ + { + "name": "model", + "type": "MODEL", + "link": 40 + }, + { + "name": "positive", + "type": "CONDITIONING", + "link": 58 + }, + { + "name": "negative", + "type": "CONDITIONING", + "link": 42 + }, + { + "name": "latent_image", + "type": "LATENT", + "link": 43 + } + ], + "outputs": [ + { + "name": "LATENT", + "type": "LATENT", + "slot_index": 0, + "links": [ + 38 + ] + } + ], + "properties": { + "cnr_id": "comfy-core", + "ver": "0.3.64", + "Node name for S&R": "KSampler", + "enableTabs": false, + "tabWidth": 65, + "tabXOffset": 10, + "hasSecondTab": false, + "secondTabText": "Send Back", + "secondTabOffset": 80, + "secondTabWidth": 65 + }, + "widgets_values": [ + 975873492175846, + "randomize", + 4, + 1, + "res_multistep", + "simple", + 1 + ] + }, + { + "id": 43, + "type": "VAEDecode", + "pos": [ + 795.0082216717279, + 684.388047845949 + ], + "size": [ + 210, + 46 + ], + "flags": {}, + "order": 11, + "mode": 0, + "inputs": [ + { + "name": "samples", + "type": "LATENT", + "link": 38 + }, + { + "name": "vae", + "type": "VAE", + "link": 39 + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "slot_index": 0, + "links": [ + 45 + ] + } + ], + "properties": { + "cnr_id": "comfy-core", + "ver": "0.3.64", + "Node name for S&R": "VAEDecode", + "enableTabs": false, + "tabWidth": 65, + "tabXOffset": 10, + "hasSecondTab": false, + "secondTabText": "Send Back", + "secondTabOffset": 80, + "secondTabWidth": 65 + }, + "widgets_values": [] + }, + { + "id": 39, + "type": "CLIPLoader", + "pos": [ + 114.20652660625237, + 394.99067540811785 + ], + "size": [ + 270, + 106 + ], + "flags": {}, + "order": 2, + "mode": 0, + "inputs": [], + "outputs": [ + { + "name": "CLIP", + "type": "CLIP", + "links": [ + 54 + ] + } + ], + "properties": { + "cnr_id": "comfy-core", + "ver": "0.3.73", + "Node name for S&R": "CLIPLoader", + "models": [ + { + "name": "qwen_3_4b.safetensors", + "url": "https://huggingface.co/Comfy-Org/z_image_turbo/resolve/main/split_files/text_encoders/qwen_3_4b.safetensors", + "directory": "text_encoders" + } + ], + "enableTabs": false, + "tabWidth": 65, + "tabXOffset": 10, + "hasSecondTab": false, + "secondTabText": "Send Back", + "secondTabOffset": 80, + "secondTabWidth": 65 + }, + "widgets_values": [ + "qwen_3_4b.safetensors", + "lumina2", + "default" + ] + }, + { + "id": 47, + "type": "ModelSamplingAuraFlow", + "pos": [ + 795.0082216717279, + 265.2544241927336 + ], + "size": [ + 310, + 60 + ], + "flags": {}, + "order": 6, + "mode": 4, + "inputs": [ + { + "name": "model", + "type": "MODEL", + "link": 37 + } + ], + "outputs": [ + { + "name": "MODEL", + "type": "MODEL", + "slot_index": 0, + "links": [ + 40 + ] + } + ], + "properties": { + "cnr_id": "comfy-core", + "ver": "0.3.64", + "Node name for S&R": "ModelSamplingAuraFlow", + "enableTabs": false, + "tabWidth": 65, + "tabXOffset": 10, + "hasSecondTab": false, + "secondTabText": "Send Back", + "secondTabOffset": 80, + "secondTabWidth": 65 + }, + "widgets_values": [ + 3 + ] + }, + { + "id": 9, + "type": "SaveImage", + "pos": [ + 1136.1104464457053, + 230.88009025907556 + ], + "size": [ + 537.8973890152877, + 494.16432401930524 + ], + "flags": {}, + "order": 12, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 45 + } + ], + "outputs": [], + "properties": { + "cnr_id": "comfy-core", + "ver": "0.3.64", + "Node name for S&R": "SaveImage", + "enableTabs": false, + "tabWidth": 65, + "tabXOffset": 10, + "hasSecondTab": false, + "secondTabText": "Send Back", + "secondTabOffset": 80, + "secondTabWidth": 65 + }, + "widgets_values": [ + "z-image" + ] + }, + { + "id": 57, + "type": "CLIPTextEncode", + "pos": [ + 415.3044150084496, + 546.4130835823802 + ], + "size": [ + 355.75992678636476, + 88 + ], + "flags": {}, + "order": 7, + "mode": 0, + "inputs": [ + { + "name": "clip", + "type": "CLIP", + "link": 54 + }, + { + "name": "text", + "type": "STRING", + "widget": { + "name": "text" + }, + "link": 56 + } + ], + "outputs": [ + { + "name": "CONDITIONING", + "type": "CONDITIONING", + "links": [ + 55, + 58 + ] + } + ], + "properties": { + "cnr_id": "comfy-core", + "ver": "0.11.0", + "Node name for S&R": "CLIPTextEncode" + }, + "widgets_values": [ + "" + ], + "color": "#232", + "bgcolor": "#353" + }, + { + "id": 42, + "type": "ConditioningZeroOut", + "pos": [ + 416.84707291063813, + 675.4490939975618 + ], + "size": [ + 204.134765625, + 26 + ], + "flags": { + "collapsed": false + }, + "order": 9, + "mode": 0, + "inputs": [ + { + "name": "conditioning", + "type": "CONDITIONING", + "link": 55 + } + ], + "outputs": [ + { + "name": "CONDITIONING", + "type": "CONDITIONING", + "links": [ + 42 + ] + } + ], + "properties": { + "cnr_id": "comfy-core", + "ver": "0.3.73", + "Node name for S&R": "ConditioningZeroOut", + "enableTabs": false, + "tabWidth": 65, + "tabXOffset": 10, + "hasSecondTab": false, + "secondTabText": "Send Back", + "secondTabOffset": 80, + "secondTabWidth": 65 + }, + "widgets_values": [] + }, + { + "id": 35, + "type": "MarkdownNote", + "pos": [ + -325.38442194119773, + 228.58407683931307 + ], + "size": [ + 411.7976824951886, + 386.7969751034775 + ], + "flags": { + "collapsed": false + }, + "order": 3, + "mode": 0, + "inputs": [], + "outputs": [], + "title": "Model link", + "properties": {}, + "widgets_values": [ + "## Report issue\n\nIf you found any issues when running this workflow, [report template issue here](https://github.com/Comfy-Org/workflow_templates/issues)\n\n\n## Model links\n\n**text_encoders**\n\n- [qwen_3_4b.safetensors](https://huggingface.co/Comfy-Org/z_image_turbo/resolve/main/split_files/text_encoders/qwen_3_4b.safetensors)\n\n**diffusion_models**\n\n- [z_image_turbo_bf16.safetensors](https://huggingface.co/Comfy-Org/z_image_turbo/resolve/main/split_files/diffusion_models/z_image_turbo_bf16.safetensors)\n\n**vae**\n\n- [ae.safetensors](https://huggingface.co/Comfy-Org/z_image_turbo/resolve/main/split_files/vae/ae.safetensors)\n\n\nModel Storage Location\n\n```\n📂 ComfyUI/\n├── 📂 models/\n│ ├── 📂 text_encoders/\n│ │ └── qwen_3_4b.safetensors\n│ ├── 📂 diffusion_models/\n│ │ └── z_image_turbo_bf16.safetensors\n│ └── 📂 vae/\n│ └── ae.safetensors\n```\n\n" + ], + "color": "#432", + "bgcolor": "#653" + }, + { + "id": 41, + "type": "EmptySD3LatentImage", + "pos": [ + 115.66690305358917, + 693.0199623783967 + ], + "size": [ + 260, + 110 + ], + "flags": {}, + "order": 4, + "mode": 0, + "inputs": [], + "outputs": [ + { + "name": "LATENT", + "type": "LATENT", + "slot_index": 0, + "links": [ + 43 + ] + } + ], + "properties": { + "cnr_id": "comfy-core", + "ver": "0.3.64", + "Node name for S&R": "EmptySD3LatentImage", + "enableTabs": false, + "tabWidth": 65, + "tabXOffset": 10, + "hasSecondTab": false, + "secondTabText": "Send Back", + "secondTabOffset": 80, + "secondTabWidth": 65 + }, + "widgets_values": [ + 1024, + 576, + 1 + ] + }, + { + "id": 56, + "type": "SimplePromptBatcher", + "pos": [ + 405.21551002213346, + 221.44309846775698 + ], + "size": [ + 364.4321427873508, + 283.66376369960267 + ], + "flags": {}, + "order": 5, + "mode": 0, + "inputs": [], + "outputs": [ + { + "name": "prompt", + "shape": 6, + "type": "STRING", + "links": [ + 56, + 57 + ] + } + ], + "properties": { + "cnr_id": "comfyui-simple-prompt-batcher", + "ver": "1.0.0", + "Node name for S&R": "SimplePromptBatcher", + "ue_properties": { + "widget_ue_connectable": {}, + "input_ue_unconnectable": {}, + "version": "7.5.2" + } + }, + "widgets_values": [ + "a room with neon light, a teddybear", + "A 42-year-old woman with greasy shoulder-length black hair, wearing a frayed lavender robe tied crookedly, stands barefoot in a dim kitchen, staring into an open refrigerator glowing faintly blue. Inside, dozens of identical eggs float in midair, each containing a tiny screaming face. The only light comes from the fridge’s dying bulb, casting long shadows that twitch like living things on the cracked linoleum.\n\nA 6-year-old boy with matted blond curls and a too-large flannel shirt sits cross-legged on a bathroom floor, pressing his forehead against the mirror as his reflection blinks without him. Water drips from the faucet in slow, heavy drops—each splash echoes like a heartbeat. The tiles are covered in peeling stickers of smiling animals, their eyes scratched out.\n\nA man in his late 50s with a shaved head and a stained white lab coat stands motionless beneath a flickering fluorescent tube in an endless corridor lined with identical doors. In his hand: a single key that doesn’t belong to any lock. The air smells of burnt sugar and wet wool. Every door handle is rusted shut, except one—slightly ajar, whispering.\n", + "surrealistic, worn look, strong colors, dramatic lighting, unreal engine, rendering, cinematic composition, highly detailed" + ], + "color": "#232", + "bgcolor": "#353" + }, + { + "id": 58, + "type": "ShowText|pysssss", + "pos": [ + 423.00661112698293, + 746.4698956378583 + ], + "size": [ + 345.5792930551006, + 518.2557719909985 + ], + "flags": { + "collapsed": true + }, + "order": 8, + "mode": 4, + "inputs": [ + { + "name": "text", + "type": "STRING", + "link": 57 + } + ], + "outputs": [ + { + "name": "STRING", + "shape": 6, + "type": "STRING", + "links": null + } + ], + "title": "Show Text - Debugging 🐍", + "properties": { + "cnr_id": "comfyui-custom-scripts", + "ver": "1.2.5", + "Node name for S&R": "ShowText|pysssss", + "ue_properties": { + "version": "7.0.1", + "widget_ue_connectable": {} + } + }, + "widgets_values": [ + "a room with neon light, a teddybear, A 42-year-old woman with greasy shoulder-length black hair, wearing a frayed lavender robe tied crookedly, stands barefoot in a dim kitchen, staring into an open refrigerator glowing faintly blue. Inside, dozens of identical eggs float in midair, each containing a tiny screaming face. The only light comes from the fridge’s dying bulb, casting long shadows that twitch like living things on the cracked linoleum., surrealistic, worn look, strong colors, dramatic lighting, unreal engine, rendering, cinematic composition, highly detailed", + "a room with neon light, a teddybear, A 6-year-old boy with matted blond curls and a too-large flannel shirt sits cross-legged on a bathroom floor, pressing his forehead against the mirror as his reflection blinks without him. Water drips from the faucet in slow, heavy drops—each splash echoes like a heartbeat. The tiles are covered in peeling stickers of smiling animals, their eyes scratched out., surrealistic, worn look, strong colors, dramatic lighting, unreal engine, rendering, cinematic composition, highly detailed", + "a room with neon light, a teddybear, A man in his late 50s with a shaved head and a stained white lab coat stands motionless beneath a flickering fluorescent tube in an endless corridor lined with identical doors. In his hand: a single key that doesn’t belong to any lock. The air smells of burnt sugar and wet wool. Every door handle is rusted shut, except one—slightly ajar, whispering., surrealistic, worn look, strong colors, dramatic lighting, unreal engine, rendering, cinematic composition, highly detailed" + ], + "color": "#432", + "bgcolor": "#653" + } + ], + "links": [ + [ + 37, + 46, + 0, + 47, + 0, + "MODEL" + ], + [ + 38, + 44, + 0, + 43, + 0, + "LATENT" + ], + [ + 39, + 40, + 0, + 43, + 1, + "VAE" + ], + [ + 40, + 47, + 0, + 44, + 0, + "MODEL" + ], + [ + 42, + 42, + 0, + 44, + 2, + "CONDITIONING" + ], + [ + 43, + 41, + 0, + 44, + 3, + "LATENT" + ], + [ + 45, + 43, + 0, + 9, + 0, + "IMAGE" + ], + [ + 54, + 39, + 0, + 57, + 0, + "CLIP" + ], + [ + 55, + 57, + 0, + 42, + 0, + "CONDITIONING" + ], + [ + 56, + 56, + 0, + 57, + 1, + "STRING" + ], + [ + 57, + 56, + 0, + 58, + 0, + "STRING" + ], + [ + 58, + 57, + 0, + 44, + 1, + "CONDITIONING" + ] + ], + "groups": [ + { + "id": 2, + "title": "Step2 - Image size", + "bounding": [ + 105.66690305358918, + 623.0199623783967, + 290, + 200 + ], + "color": "#3f789e", + "font_size": 24, + "flags": {} + }, + { + "id": 4, + "title": "Step1 - Load models", + "bounding": [ + 105.1249576505395, + 195.68434121376112, + 290, + 413.6 + ], + "color": "#3f789e", + "font_size": 24, + "flags": {} + }, + { + "id": 7, + "title": "sampling", + "bounding": [ + 785.0082216717279, + 191.65442419273336, + 335.5397560057179, + 548.7336236532161 + ], + "color": "#3f789e", + "font_size": 24, + "flags": {} + } + ], + "config": {}, + "extra": { + "ds": { + "scale": 0.9531171560791476, + "offset": [ + 414.01505676533793, + -81.72652487282919 + ] + }, + "frontendVersion": "1.37.11", + "workflowRendererVersion": "LG", + "VHS_latentpreview": false, + "VHS_latentpreviewrate": 0, + "VHS_MetadataImage": true, + "VHS_KeepIntermediate": true + }, + "version": 0.4 +} \ No newline at end of file diff --git a/prompt_batcher.py b/prompt_batcher.py index 86c93c2..67b99c9 100644 --- a/prompt_batcher.py +++ b/prompt_batcher.py @@ -1,22 +1,37 @@ """ -ComfyUI Simple Prompt Batcher -Takes multiple prompts (one per line) and outputs them one at a time for batch processing +ComfyUI Simple Prompt Batcher with Global Style (Robust Version) +Takes multiple prompts (one per line) and prepends and appends a global string to all prompts for batch processing +Includes extra safeguards to prevent duplicate prompts or ComfyUI misinterpretation """ class SimplePromptBatcher: """ Simple prompt batcher - one prompt per line Automatically batches through all prompts for inference + Allows a global style to be appended to all prompts """ @classmethod def INPUT_TYPES(cls): return { "required": { + "prepend": ("STRING", { + "default": "", + "multiline": True, + "placeholder": "Text to prepend to all prompts", + "tooltip": "Text prepended to every prompt." + }), "prompts": ("STRING", { "default": "", "multiline": True, - "placeholder": "Enter prompts here\nOne prompt per line\nNo empty lines between prompts" + "placeholder": "Prompts (one per line). Enter one prompt per line. No empty lines between prompts.", + "tooltip": "Each line is treated as a separate prompt for batch processing." + }), + "append": ("STRING", { + "default": "", + "multiline": True, + "placeholder": "Text to append to all prompts", + "tooltip": "Text appended to every prompt." }), } } @@ -27,25 +42,31 @@ class SimplePromptBatcher: CATEGORY = "utils/text" OUTPUT_IS_LIST = (True,) # This makes it output as a batch - def batch_prompts(self, prompts): + def batch_prompts(self, prepend="", prompts="", append=""): """ Split prompts by newline and return as batch - Each line becomes a separate inference + Format: [prepend] + prompt + [append] """ - # Split by newlines and filter out empty lines + # Split by newline and remove empty lines / trim spaces prompt_list = [line.strip() for line in prompts.split('\n') if line.strip()] if not prompt_list: print("[Prompt Batcher] ⚠️ No prompts provided, returning empty prompt") return ([""],) + prompt_list = [ + f"{prepend}{', ' if prepend else ''}{p}{', ' if append else ''}{append}" + for p in prompt_list + ] + + # Debug: print each prompt for verification print(f"[Prompt Batcher] 📋 Batching {len(prompt_list)} prompts:") for i, prompt in enumerate(prompt_list, 1): preview = prompt[:60] + "..." if len(prompt) > 60 else prompt print(f"[Prompt Batcher] {i}. {preview}") - # Return as tuple containing list (required by ComfyUI) + # Return as tuple containing the list (required by ComfyUI) return (prompt_list,)