├── .gitignore ├── .pre-commit-config.yaml ├── LICENSE ├── README.md ├── __init__.py ├── docs └── CHANGELOG.md ├── example_workflows ├── combinatorial.json ├── feelinglucky.json ├── jinja2.json ├── magic_prompt.json ├── random.json └── workflow.json ├── images ├── example.png └── menu.png ├── install.py ├── nodes ├── __init__.py ├── combinatorial.py ├── feeling_lucky.py ├── generator.py ├── jinja.py ├── magicprompt.py ├── output_node.py ├── random.py └── sampler.py ├── pyproject.toml ├── requirements.txt └── web-extensions └── dp.js /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | __pycache__ 3 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/charliermarsh/ruff-pre-commit 3 | rev: v0.0.259 4 | hooks: 5 | - id: ruff 6 | args: 7 | - --fix 8 | - repo: https://github.com/pre-commit/pre-commit-hooks 9 | rev: v4.4.0 10 | hooks: 11 | - id: end-of-file-fixer 12 | exclude: ^collections/.* 13 | - id: trailing-whitespace 14 | exclude: ^collections/.* 15 | - repo: https://github.com/psf/black 16 | rev: 23.1.0 17 | hooks: 18 | - id: black 19 | args: 20 | - --quiet 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Adi Eyal 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ComfyUI-DynamicPrompts Custom Nodes 2 | 3 | 4 | 5 | ComfyUI-DynamicPrompts is a custom nodes library that integrates into your existing ComfyUI Library. It provides nodes that enable the use of [Dynamic Prompts](https://github.com/adieyal/dynamicprompts) in your ComfyUI. The nodes provided in this library are: 6 | 7 | 1. [Random Prompts](https://github.com/adieyal/sd-dynamic-prompts/blob/main/docs/SYNTAX.md#variants) - Implements standard wildcard mode for random sampling of variants and wildcards. 8 | 2. [Combinatorial Prompts](https://github.com/adieyal/sd-dynamic-prompts/blob/main/docs/SYNTAX.md#combinatorial-sampler) - Iterates through every possible combination of random values in a prompt. 9 | 3. [I'm Feeling Lucky](https://github.com/adieyal/dynamicprompts#im-feeling-lucky) - Uses the given prompt to download a related prompt from Lexica.art. 10 | 4. [Magic Prompt](https://github.com/adieyal/dynamicprompts#magic-prompt) - Employs a neural network to add relevant modifiers to your prompt. 11 | 5. [Jinja2 Templates](https://github.com/adieyal/dynamicprompts#jinja2-templates) - Allows you to write prompts using Jinja2 templates. 12 | 13 | Nodes can be found in the Dynamic Prompts menu: 14 | 15 | 16 | ## Useful Links 17 | * [Dynamic Prompts Home](https://github.com/adieyal/dynamicprompts) 18 | * [Dynamic Prompts Syntax](https://github.com/adieyal/sd-dynamic-prompts/blob/main/docs/SYNTAX.md) 19 | * [Dynamic Prompts Extension for Auto1111](https://github.com/adieyal/sd-dynamic-prompts) 20 | * [Tutorial](https://github.com/adieyal/sd-dynamic-prompts/blob/main/docs/tutorial.md) 21 | * [Online Resources](https://github.com/adieyal/sd-dynamic-prompts/blob/main/docs/resources.md) 22 | * [CHANGELOG](./CHANGELOG.md) 23 | * [Stable Diffusion SDXL Wildcards and ComfyUI](https://ericri.medium.com/stable-diffusion-sdxl-wildcards-and-comfyui-e8483e00e1c) 24 | 25 | ## Installation 26 | 27 | ### Using ComfyUI-Manager 28 | 1. Install [ComfyUI-Manager](https://github.com/ltdrdata/ComfyUI-Manager) if it isn't already. 29 | 2. Press Install Custom Nodes from the ComfyUI-Manager menu 30 | 3. Search for dynamicprompts 31 | 4. Click install 32 | 33 | ### Manual installation 34 | 35 | Follow the steps below to install the ComfyUI-DynamicPrompts Library. These commands assume the your current working directory is the ComfyUI root directory. 36 | 37 | 1. Clone the repository: 38 | ``` 39 | git clone https://github.com/adieyal/comfyui-dynamicprompts custom_nodes/comfyui-dynamicprompts 40 | ``` 41 | 2. Install the required Python packages: 42 | ``` 43 | python -m pip install -r custom_nodes/comfyui-dynamicprompts/requirements.txt 44 | ``` 45 | 3. Run install.py: 46 | ``` 47 | python custom_nodes/comfyui-dynamicprompts/install.py 48 | ``` 49 | 4. `mkdir custom_nodes/comfyui-dynamicprompts/wildcards` 50 | 5. (Optional) download a wildcard collection and copy it into this new wildcards folder. 51 | 6. Restart your ComfyUI. 52 | 7. Example workflows can be found in `custom_nodes/comfyui-dynamicprompts/example_workflows`. 53 | 54 | ## Wildcards 55 | The extension looks for wildcard files in `custom_nodes/comfyui-dynamicprompts/wildcards`. It supports wildcard files in `.txt`, .`json` and .`yaml` ([example format](https://github.com/adieyal/sd-dynamic-prompts/blob/main/collections/publicprompts.yaml)). 56 | 57 | There are many pre-built wildcards collections available. Here are a few to get you started: 58 | * [SD Dynamic Prompts built-in](https://github.com/adieyal/sd-dynamic-prompts/tree/main/collections) 59 | * [Civitai wildcard packs](https://github.com/adieyal/sd-dynamic-prompts/blob/main/docs/resources.md#wildcard-packs) 60 | 61 | 62 | ## Node Outputs 63 | 64 | All nodes in this library produce a String output that can typically be passed into Clip Text Encode Prompts. Both positive and negative nodes are supported. 65 | 66 | Please note, since ComfyUI is inherently stateless, some nodes might have a slightly unexpected behavior: 67 | 68 | - The Combinatorial Prompt generation iterates through all possible values in a cycle. For example, A `{red|green|blue}` ball will generate the following sequence: 69 | - A red ball 70 | - A green ball 71 | - A blue ball 72 | - A red ball 73 | - ... 74 | 75 | - The I'm Feeling Lucky, Magic Prompt, and Jinja2 nodes have an optional auto refresh parameter. If set to `True`, a new prompt is generated for every iteration. However, if set to `False`, a prompt will be downloaded once and used for subsequent generations. 76 | 77 | ## Roadmap 78 | 79 | 1. This is a quick and dirty release with some known limitations such as non-configurable random seeds, inability to select the MagicPrompt model, and absence of several other configuration features. 80 | 2. Currently, the only way to view the generated prompt is through console output. A future release will introduce nodes to display the generate prompt on screen and to write it to a file. 81 | 3. It is not currently possible to chain generators, this functionality will be added in future releases. 82 | 5. The installation script will be automated in the future for ease of use. 83 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | from .nodes import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS 2 | 3 | __version__ = "0.2.1" 4 | 5 | __all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS"] 6 | -------------------------------------------------------------------------------- /docs/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 0.2.1 - Fixed a bug due to a backwards incompatible change in the latest version of dynamicprompts that returns a SampleResult instead of a str 2 | 0.2.0 - Nodes now have settable seeds for reproducibility. Thanks to [@romeobuilderotti](https://github.com/romeobuilderotti/) for the inspiration for the inspiration. Also upgraded dynamicprompts to v0.30.1 3 | 0.1.3 - Wildcards in the ComfUI root are also supported for backwards compatibility 4 | 0.1.2 - The extension now looks for wildcards in custom_nodes/comfyui_dynamicprompts/wildcards 5 | 0.1.1 - Changed to relative imports - fixes [#1](https://github.com/adieyal/comfyui-dynamicprompts/issues/1) 6 | 0.1.0 - First release 7 | -------------------------------------------------------------------------------- /example_workflows/combinatorial.json: -------------------------------------------------------------------------------- 1 | { 2 | "last_node_id": 11, 3 | "last_link_id": 11, 4 | "nodes": [ 5 | { 6 | "id": 4, 7 | "type": "CheckpointLoaderSimple", 8 | "pos": [ 9 | -117, 10 | 491 11 | ], 12 | "size": { 13 | "0": 315, 14 | "1": 98 15 | }, 16 | "flags": {}, 17 | "order": 0, 18 | "mode": 0, 19 | "outputs": [ 20 | { 21 | "name": "MODEL", 22 | "type": "MODEL", 23 | "links": [ 24 | 1 25 | ], 26 | "slot_index": 0 27 | }, 28 | { 29 | "name": "CLIP", 30 | "type": "CLIP", 31 | "links": [ 32 | 3, 33 | 5 34 | ], 35 | "slot_index": 1 36 | }, 37 | { 38 | "name": "VAE", 39 | "type": "VAE", 40 | "links": [ 41 | 8 42 | ], 43 | "slot_index": 2 44 | } 45 | ], 46 | "properties": { 47 | "Node name for S&R": "CheckpointLoaderSimple" 48 | }, 49 | "widgets_values": [ 50 | "v1-5-pruned-emaonly.ckpt" 51 | ] 52 | }, 53 | { 54 | "id": 3, 55 | "type": "KSampler", 56 | "pos": [ 57 | 749, 58 | 186 59 | ], 60 | "size": { 61 | "0": 315, 62 | "1": 262 63 | }, 64 | "flags": { 65 | "collapsed": true 66 | }, 67 | "order": 5, 68 | "mode": 0, 69 | "inputs": [ 70 | { 71 | "name": "model", 72 | "type": "MODEL", 73 | "link": 1 74 | }, 75 | { 76 | "name": "positive", 77 | "type": "CONDITIONING", 78 | "link": 4 79 | }, 80 | { 81 | "name": "negative", 82 | "type": "CONDITIONING", 83 | "link": 6 84 | }, 85 | { 86 | "name": "latent_image", 87 | "type": "LATENT", 88 | "link": 2 89 | } 90 | ], 91 | "outputs": [ 92 | { 93 | "name": "LATENT", 94 | "type": "LATENT", 95 | "links": [ 96 | 7 97 | ], 98 | "slot_index": 0 99 | } 100 | ], 101 | "properties": { 102 | "Node name for S&R": "KSampler" 103 | }, 104 | "widgets_values": [ 105 | 649510431673174, 106 | "randomize", 107 | 20, 108 | 8, 109 | "euler", 110 | "normal", 111 | 1 112 | ] 113 | }, 114 | { 115 | "id": 8, 116 | "type": "VAEDecode", 117 | "pos": [ 118 | 974, 119 | 183 120 | ], 121 | "size": { 122 | "0": 210, 123 | "1": 46 124 | }, 125 | "flags": { 126 | "collapsed": true 127 | }, 128 | "order": 6, 129 | "mode": 0, 130 | "inputs": [ 131 | { 132 | "name": "samples", 133 | "type": "LATENT", 134 | "link": 7 135 | }, 136 | { 137 | "name": "vae", 138 | "type": "VAE", 139 | "link": 8 140 | } 141 | ], 142 | "outputs": [ 143 | { 144 | "name": "IMAGE", 145 | "type": "IMAGE", 146 | "links": [ 147 | 9 148 | ], 149 | "slot_index": 0 150 | } 151 | ], 152 | "properties": { 153 | "Node name for S&R": "VAEDecode" 154 | } 155 | }, 156 | { 157 | "id": 7, 158 | "type": "CLIPTextEncode", 159 | "pos": [ 160 | 503, 161 | 538 162 | ], 163 | "size": { 164 | "0": 425.27801513671875, 165 | "1": 180.6060791015625 166 | }, 167 | "flags": { 168 | "collapsed": true 169 | }, 170 | "order": 3, 171 | "mode": 0, 172 | "inputs": [ 173 | { 174 | "name": "clip", 175 | "type": "CLIP", 176 | "link": 5 177 | } 178 | ], 179 | "outputs": [ 180 | { 181 | "name": "CONDITIONING", 182 | "type": "CONDITIONING", 183 | "links": [ 184 | 6 185 | ], 186 | "slot_index": 0 187 | } 188 | ], 189 | "properties": { 190 | "Node name for S&R": "CLIPTextEncode" 191 | }, 192 | "widgets_values": [ 193 | "text, watermark" 194 | ] 195 | }, 196 | { 197 | "id": 5, 198 | "type": "EmptyLatentImage", 199 | "pos": [ 200 | 388, 201 | 191 202 | ], 203 | "size": { 204 | "0": 315, 205 | "1": 106 206 | }, 207 | "flags": { 208 | "collapsed": true 209 | }, 210 | "order": 1, 211 | "mode": 0, 212 | "outputs": [ 213 | { 214 | "name": "LATENT", 215 | "type": "LATENT", 216 | "links": [ 217 | 2 218 | ], 219 | "slot_index": 0 220 | } 221 | ], 222 | "properties": { 223 | "Node name for S&R": "EmptyLatentImage" 224 | }, 225 | "widgets_values": [ 226 | 512, 227 | 512, 228 | 1 229 | ] 230 | }, 231 | { 232 | "id": 9, 233 | "type": "SaveImage", 234 | "pos": [ 235 | 1212, 236 | 154 237 | ], 238 | "size": { 239 | "0": 210, 240 | "1": 270 241 | }, 242 | "flags": {}, 243 | "order": 7, 244 | "mode": 0, 245 | "inputs": [ 246 | { 247 | "name": "images", 248 | "type": "IMAGE", 249 | "link": 9 250 | } 251 | ], 252 | "properties": {}, 253 | "widgets_values": [ 254 | "ComfyUI" 255 | ] 256 | }, 257 | { 258 | "id": 6, 259 | "type": "CLIPTextEncode", 260 | "pos": [ 261 | 406, 262 | 56 263 | ], 264 | "size": { 265 | "0": 422.84503173828125, 266 | "1": 164.31304931640625 267 | }, 268 | "flags": { 269 | "collapsed": true 270 | }, 271 | "order": 4, 272 | "mode": 0, 273 | "inputs": [ 274 | { 275 | "name": "clip", 276 | "type": "CLIP", 277 | "link": 3 278 | }, 279 | { 280 | "name": "text", 281 | "type": "STRING", 282 | "link": 11, 283 | "widget": { 284 | "name": "text", 285 | "config": [ 286 | "STRING", 287 | { 288 | "multiline": true 289 | } 290 | ] 291 | } 292 | } 293 | ], 294 | "outputs": [ 295 | { 296 | "name": "CONDITIONING", 297 | "type": "CONDITIONING", 298 | "links": [ 299 | 4 300 | ], 301 | "slot_index": 0 302 | } 303 | ], 304 | "properties": { 305 | "Node name for S&R": "CLIPTextEncode" 306 | }, 307 | "widgets_values": [ 308 | "beautiful scenery nature glass bottle landscape, , purple galaxy bottle," 309 | ] 310 | }, 311 | { 312 | "id": 11, 313 | "type": "DPCombinatorialGenerator", 314 | "pos": [ 315 | -245, 316 | 28 317 | ], 318 | "size": { 319 | "0": 400, 320 | "1": 200 321 | }, 322 | "flags": {}, 323 | "order": 2, 324 | "mode": 0, 325 | "outputs": [ 326 | { 327 | "name": "STRING", 328 | "type": "STRING", 329 | "links": [ 330 | 11 331 | ], 332 | "shape": 3, 333 | "slot_index": 0 334 | } 335 | ], 336 | "title": "DP - Combinatorial", 337 | "properties": { 338 | "Node name for S&R": "DPCombinatorialGenerator" 339 | }, 340 | "widgets_values": [ 341 | "A {red|green|blue} ball by __artists/Photography/by_country/china__" 342 | ], 343 | "color": "#2a363b", 344 | "bgcolor": "#3f5159" 345 | } 346 | ], 347 | "links": [ 348 | [ 349 | 1, 350 | 4, 351 | 0, 352 | 3, 353 | 0, 354 | "MODEL" 355 | ], 356 | [ 357 | 2, 358 | 5, 359 | 0, 360 | 3, 361 | 3, 362 | "LATENT" 363 | ], 364 | [ 365 | 3, 366 | 4, 367 | 1, 368 | 6, 369 | 0, 370 | "CLIP" 371 | ], 372 | [ 373 | 4, 374 | 6, 375 | 0, 376 | 3, 377 | 1, 378 | "CONDITIONING" 379 | ], 380 | [ 381 | 5, 382 | 4, 383 | 1, 384 | 7, 385 | 0, 386 | "CLIP" 387 | ], 388 | [ 389 | 6, 390 | 7, 391 | 0, 392 | 3, 393 | 2, 394 | "CONDITIONING" 395 | ], 396 | [ 397 | 7, 398 | 3, 399 | 0, 400 | 8, 401 | 0, 402 | "LATENT" 403 | ], 404 | [ 405 | 8, 406 | 4, 407 | 2, 408 | 8, 409 | 1, 410 | "VAE" 411 | ], 412 | [ 413 | 9, 414 | 8, 415 | 0, 416 | 9, 417 | 0, 418 | "IMAGE" 419 | ], 420 | [ 421 | 11, 422 | 11, 423 | 0, 424 | 6, 425 | 1, 426 | "STRING" 427 | ] 428 | ], 429 | "groups": [], 430 | "config": {}, 431 | "extra": {}, 432 | "version": 0.4 433 | } 434 | -------------------------------------------------------------------------------- /example_workflows/feelinglucky.json: -------------------------------------------------------------------------------- 1 | { 2 | "last_node_id": 12, 3 | "last_link_id": 12, 4 | "nodes": [ 5 | { 6 | "id": 4, 7 | "type": "CheckpointLoaderSimple", 8 | "pos": [ 9 | -117, 10 | 491 11 | ], 12 | "size": { 13 | "0": 315, 14 | "1": 98 15 | }, 16 | "flags": {}, 17 | "order": 0, 18 | "mode": 0, 19 | "outputs": [ 20 | { 21 | "name": "MODEL", 22 | "type": "MODEL", 23 | "links": [ 24 | 1 25 | ], 26 | "slot_index": 0 27 | }, 28 | { 29 | "name": "CLIP", 30 | "type": "CLIP", 31 | "links": [ 32 | 3, 33 | 5 34 | ], 35 | "slot_index": 1 36 | }, 37 | { 38 | "name": "VAE", 39 | "type": "VAE", 40 | "links": [ 41 | 8 42 | ], 43 | "slot_index": 2 44 | } 45 | ], 46 | "properties": { 47 | "Node name for S&R": "CheckpointLoaderSimple" 48 | }, 49 | "widgets_values": [ 50 | "v1-5-pruned-emaonly.ckpt" 51 | ] 52 | }, 53 | { 54 | "id": 3, 55 | "type": "KSampler", 56 | "pos": [ 57 | 749, 58 | 186 59 | ], 60 | "size": { 61 | "0": 315, 62 | "1": 262 63 | }, 64 | "flags": { 65 | "collapsed": true 66 | }, 67 | "order": 5, 68 | "mode": 0, 69 | "inputs": [ 70 | { 71 | "name": "model", 72 | "type": "MODEL", 73 | "link": 1 74 | }, 75 | { 76 | "name": "positive", 77 | "type": "CONDITIONING", 78 | "link": 4 79 | }, 80 | { 81 | "name": "negative", 82 | "type": "CONDITIONING", 83 | "link": 6 84 | }, 85 | { 86 | "name": "latent_image", 87 | "type": "LATENT", 88 | "link": 2 89 | } 90 | ], 91 | "outputs": [ 92 | { 93 | "name": "LATENT", 94 | "type": "LATENT", 95 | "links": [ 96 | 7 97 | ], 98 | "slot_index": 0 99 | } 100 | ], 101 | "properties": { 102 | "Node name for S&R": "KSampler" 103 | }, 104 | "widgets_values": [ 105 | 921027108188161, 106 | "randomize", 107 | 20, 108 | 8, 109 | "euler", 110 | "normal", 111 | 1 112 | ] 113 | }, 114 | { 115 | "id": 8, 116 | "type": "VAEDecode", 117 | "pos": [ 118 | 974, 119 | 183 120 | ], 121 | "size": { 122 | "0": 210, 123 | "1": 46 124 | }, 125 | "flags": { 126 | "collapsed": true 127 | }, 128 | "order": 6, 129 | "mode": 0, 130 | "inputs": [ 131 | { 132 | "name": "samples", 133 | "type": "LATENT", 134 | "link": 7 135 | }, 136 | { 137 | "name": "vae", 138 | "type": "VAE", 139 | "link": 8 140 | } 141 | ], 142 | "outputs": [ 143 | { 144 | "name": "IMAGE", 145 | "type": "IMAGE", 146 | "links": [ 147 | 9 148 | ], 149 | "slot_index": 0 150 | } 151 | ], 152 | "properties": { 153 | "Node name for S&R": "VAEDecode" 154 | } 155 | }, 156 | { 157 | "id": 7, 158 | "type": "CLIPTextEncode", 159 | "pos": [ 160 | 503, 161 | 538 162 | ], 163 | "size": { 164 | "0": 425.27801513671875, 165 | "1": 180.6060791015625 166 | }, 167 | "flags": { 168 | "collapsed": true 169 | }, 170 | "order": 3, 171 | "mode": 0, 172 | "inputs": [ 173 | { 174 | "name": "clip", 175 | "type": "CLIP", 176 | "link": 5 177 | } 178 | ], 179 | "outputs": [ 180 | { 181 | "name": "CONDITIONING", 182 | "type": "CONDITIONING", 183 | "links": [ 184 | 6 185 | ], 186 | "slot_index": 0 187 | } 188 | ], 189 | "properties": { 190 | "Node name for S&R": "CLIPTextEncode" 191 | }, 192 | "widgets_values": [ 193 | "text, watermark" 194 | ] 195 | }, 196 | { 197 | "id": 5, 198 | "type": "EmptyLatentImage", 199 | "pos": [ 200 | 388, 201 | 191 202 | ], 203 | "size": { 204 | "0": 315, 205 | "1": 106 206 | }, 207 | "flags": { 208 | "collapsed": true 209 | }, 210 | "order": 1, 211 | "mode": 0, 212 | "outputs": [ 213 | { 214 | "name": "LATENT", 215 | "type": "LATENT", 216 | "links": [ 217 | 2 218 | ], 219 | "slot_index": 0 220 | } 221 | ], 222 | "properties": { 223 | "Node name for S&R": "EmptyLatentImage" 224 | }, 225 | "widgets_values": [ 226 | 512, 227 | 512, 228 | 1 229 | ] 230 | }, 231 | { 232 | "id": 9, 233 | "type": "SaveImage", 234 | "pos": [ 235 | 1212, 236 | 154 237 | ], 238 | "size": { 239 | "0": 210, 240 | "1": 270 241 | }, 242 | "flags": {}, 243 | "order": 7, 244 | "mode": 0, 245 | "inputs": [ 246 | { 247 | "name": "images", 248 | "type": "IMAGE", 249 | "link": 9 250 | } 251 | ], 252 | "properties": {}, 253 | "widgets_values": [ 254 | "ComfyUI" 255 | ] 256 | }, 257 | { 258 | "id": 6, 259 | "type": "CLIPTextEncode", 260 | "pos": [ 261 | 364, 262 | 77 263 | ], 264 | "size": { 265 | "0": 422.84503173828125, 266 | "1": 164.31304931640625 267 | }, 268 | "flags": { 269 | "collapsed": true 270 | }, 271 | "order": 4, 272 | "mode": 0, 273 | "inputs": [ 274 | { 275 | "name": "clip", 276 | "type": "CLIP", 277 | "link": 3 278 | }, 279 | { 280 | "name": "text", 281 | "type": "STRING", 282 | "link": 12, 283 | "widget": { 284 | "name": "text", 285 | "config": [ 286 | "STRING", 287 | { 288 | "multiline": true 289 | } 290 | ] 291 | } 292 | } 293 | ], 294 | "outputs": [ 295 | { 296 | "name": "CONDITIONING", 297 | "type": "CONDITIONING", 298 | "links": [ 299 | 4 300 | ], 301 | "slot_index": 0 302 | } 303 | ], 304 | "properties": { 305 | "Node name for S&R": "CLIPTextEncode" 306 | }, 307 | "widgets_values": [ 308 | "beautiful scenery nature glass bottle landscape, , purple galaxy bottle," 309 | ] 310 | }, 311 | { 312 | "id": 12, 313 | "type": "DPFeelingLucky", 314 | "pos": [ 315 | -193, 316 | 50 317 | ], 318 | "size": { 319 | "0": 400, 320 | "1": 200 321 | }, 322 | "flags": {}, 323 | "order": 2, 324 | "mode": 0, 325 | "outputs": [ 326 | { 327 | "name": "STRING", 328 | "type": "STRING", 329 | "links": [ 330 | 12 331 | ], 332 | "shape": 3, 333 | "slot_index": 0 334 | } 335 | ], 336 | "title": "I'm Feeling Lucky", 337 | "properties": { 338 | "Node name for S&R": "DPFeelingLucky" 339 | }, 340 | "widgets_values": [ 341 | "A mech warrior", 342 | "Yes" 343 | ], 344 | "color": "#2a363b", 345 | "bgcolor": "#3f5159" 346 | } 347 | ], 348 | "links": [ 349 | [ 350 | 1, 351 | 4, 352 | 0, 353 | 3, 354 | 0, 355 | "MODEL" 356 | ], 357 | [ 358 | 2, 359 | 5, 360 | 0, 361 | 3, 362 | 3, 363 | "LATENT" 364 | ], 365 | [ 366 | 3, 367 | 4, 368 | 1, 369 | 6, 370 | 0, 371 | "CLIP" 372 | ], 373 | [ 374 | 4, 375 | 6, 376 | 0, 377 | 3, 378 | 1, 379 | "CONDITIONING" 380 | ], 381 | [ 382 | 5, 383 | 4, 384 | 1, 385 | 7, 386 | 0, 387 | "CLIP" 388 | ], 389 | [ 390 | 6, 391 | 7, 392 | 0, 393 | 3, 394 | 2, 395 | "CONDITIONING" 396 | ], 397 | [ 398 | 7, 399 | 3, 400 | 0, 401 | 8, 402 | 0, 403 | "LATENT" 404 | ], 405 | [ 406 | 8, 407 | 4, 408 | 2, 409 | 8, 410 | 1, 411 | "VAE" 412 | ], 413 | [ 414 | 9, 415 | 8, 416 | 0, 417 | 9, 418 | 0, 419 | "IMAGE" 420 | ], 421 | [ 422 | 12, 423 | 12, 424 | 0, 425 | 6, 426 | 1, 427 | "STRING" 428 | ] 429 | ], 430 | "groups": [], 431 | "config": {}, 432 | "extra": {}, 433 | "version": 0.4 434 | } 435 | -------------------------------------------------------------------------------- /example_workflows/jinja2.json: -------------------------------------------------------------------------------- 1 | { 2 | "last_node_id": 15, 3 | "last_link_id": 14, 4 | "nodes": [ 5 | { 6 | "id": 4, 7 | "type": "CheckpointLoaderSimple", 8 | "pos": [ 9 | -117, 10 | 491 11 | ], 12 | "size": { 13 | "0": 315, 14 | "1": 98 15 | }, 16 | "flags": {}, 17 | "order": 0, 18 | "mode": 0, 19 | "outputs": [ 20 | { 21 | "name": "MODEL", 22 | "type": "MODEL", 23 | "links": [ 24 | 1 25 | ], 26 | "slot_index": 0 27 | }, 28 | { 29 | "name": "CLIP", 30 | "type": "CLIP", 31 | "links": [ 32 | 3, 33 | 5 34 | ], 35 | "slot_index": 1 36 | }, 37 | { 38 | "name": "VAE", 39 | "type": "VAE", 40 | "links": [ 41 | 8 42 | ], 43 | "slot_index": 2 44 | } 45 | ], 46 | "properties": { 47 | "Node name for S&R": "CheckpointLoaderSimple" 48 | }, 49 | "widgets_values": [ 50 | "v1-5-pruned-emaonly.ckpt" 51 | ] 52 | }, 53 | { 54 | "id": 3, 55 | "type": "KSampler", 56 | "pos": [ 57 | 749, 58 | 186 59 | ], 60 | "size": { 61 | "0": 315, 62 | "1": 262 63 | }, 64 | "flags": { 65 | "collapsed": true 66 | }, 67 | "order": 5, 68 | "mode": 0, 69 | "inputs": [ 70 | { 71 | "name": "model", 72 | "type": "MODEL", 73 | "link": 1 74 | }, 75 | { 76 | "name": "positive", 77 | "type": "CONDITIONING", 78 | "link": 4 79 | }, 80 | { 81 | "name": "negative", 82 | "type": "CONDITIONING", 83 | "link": 6 84 | }, 85 | { 86 | "name": "latent_image", 87 | "type": "LATENT", 88 | "link": 2 89 | } 90 | ], 91 | "outputs": [ 92 | { 93 | "name": "LATENT", 94 | "type": "LATENT", 95 | "links": [ 96 | 7 97 | ], 98 | "slot_index": 0 99 | } 100 | ], 101 | "properties": { 102 | "Node name for S&R": "KSampler" 103 | }, 104 | "widgets_values": [ 105 | 692806126811304, 106 | "randomize", 107 | 20, 108 | 8, 109 | "euler", 110 | "normal", 111 | 1 112 | ] 113 | }, 114 | { 115 | "id": 8, 116 | "type": "VAEDecode", 117 | "pos": [ 118 | 974, 119 | 183 120 | ], 121 | "size": { 122 | "0": 210, 123 | "1": 46 124 | }, 125 | "flags": { 126 | "collapsed": true 127 | }, 128 | "order": 6, 129 | "mode": 0, 130 | "inputs": [ 131 | { 132 | "name": "samples", 133 | "type": "LATENT", 134 | "link": 7 135 | }, 136 | { 137 | "name": "vae", 138 | "type": "VAE", 139 | "link": 8 140 | } 141 | ], 142 | "outputs": [ 143 | { 144 | "name": "IMAGE", 145 | "type": "IMAGE", 146 | "links": [ 147 | 9 148 | ], 149 | "slot_index": 0 150 | } 151 | ], 152 | "properties": { 153 | "Node name for S&R": "VAEDecode" 154 | } 155 | }, 156 | { 157 | "id": 7, 158 | "type": "CLIPTextEncode", 159 | "pos": [ 160 | 503, 161 | 538 162 | ], 163 | "size": { 164 | "0": 425.27801513671875, 165 | "1": 180.6060791015625 166 | }, 167 | "flags": { 168 | "collapsed": true 169 | }, 170 | "order": 3, 171 | "mode": 0, 172 | "inputs": [ 173 | { 174 | "name": "clip", 175 | "type": "CLIP", 176 | "link": 5 177 | } 178 | ], 179 | "outputs": [ 180 | { 181 | "name": "CONDITIONING", 182 | "type": "CONDITIONING", 183 | "links": [ 184 | 6 185 | ], 186 | "slot_index": 0 187 | } 188 | ], 189 | "properties": { 190 | "Node name for S&R": "CLIPTextEncode" 191 | }, 192 | "widgets_values": [ 193 | "text, watermark" 194 | ] 195 | }, 196 | { 197 | "id": 5, 198 | "type": "EmptyLatentImage", 199 | "pos": [ 200 | 388, 201 | 191 202 | ], 203 | "size": { 204 | "0": 315, 205 | "1": 106 206 | }, 207 | "flags": { 208 | "collapsed": true 209 | }, 210 | "order": 1, 211 | "mode": 0, 212 | "outputs": [ 213 | { 214 | "name": "LATENT", 215 | "type": "LATENT", 216 | "links": [ 217 | 2 218 | ], 219 | "slot_index": 0 220 | } 221 | ], 222 | "properties": { 223 | "Node name for S&R": "EmptyLatentImage" 224 | }, 225 | "widgets_values": [ 226 | 512, 227 | 512, 228 | 1 229 | ] 230 | }, 231 | { 232 | "id": 9, 233 | "type": "SaveImage", 234 | "pos": [ 235 | 1212, 236 | 154 237 | ], 238 | "size": { 239 | "0": 210, 240 | "1": 270 241 | }, 242 | "flags": {}, 243 | "order": 7, 244 | "mode": 0, 245 | "inputs": [ 246 | { 247 | "name": "images", 248 | "type": "IMAGE", 249 | "link": 9 250 | } 251 | ], 252 | "properties": {}, 253 | "widgets_values": [ 254 | "ComfyUI" 255 | ] 256 | }, 257 | { 258 | "id": 6, 259 | "type": "CLIPTextEncode", 260 | "pos": [ 261 | 364, 262 | 77 263 | ], 264 | "size": { 265 | "0": 422.84503173828125, 266 | "1": 164.31304931640625 267 | }, 268 | "flags": { 269 | "collapsed": true 270 | }, 271 | "order": 4, 272 | "mode": 0, 273 | "inputs": [ 274 | { 275 | "name": "clip", 276 | "type": "CLIP", 277 | "link": 3 278 | }, 279 | { 280 | "name": "text", 281 | "type": "STRING", 282 | "link": 14, 283 | "widget": { 284 | "name": "text", 285 | "config": [ 286 | "STRING", 287 | { 288 | "multiline": true 289 | } 290 | ] 291 | } 292 | } 293 | ], 294 | "outputs": [ 295 | { 296 | "name": "CONDITIONING", 297 | "type": "CONDITIONING", 298 | "links": [ 299 | 4 300 | ], 301 | "slot_index": 0 302 | } 303 | ], 304 | "properties": { 305 | "Node name for S&R": "CLIPTextEncode" 306 | }, 307 | "widgets_values": [ 308 | "beautiful scenery nature glass bottle landscape, , purple galaxy bottle," 309 | ] 310 | }, 311 | { 312 | "id": 15, 313 | "type": "DPJinja", 314 | "pos": [ 315 | -209, 316 | 50 317 | ], 318 | "size": { 319 | "0": 400, 320 | "1": 200 321 | }, 322 | "flags": {}, 323 | "order": 2, 324 | "mode": 0, 325 | "outputs": [ 326 | { 327 | "name": "STRING", 328 | "type": "STRING", 329 | "links": [ 330 | 14 331 | ], 332 | "shape": 3, 333 | "slot_index": 0 334 | } 335 | ], 336 | "properties": { 337 | "Node name for S&R": "DPJinja" 338 | }, 339 | "widgets_values": [ 340 | "{{ choice('red', 'blue', 'green') }} ball", 341 | "Yes" 342 | ], 343 | "color": "#2a363b", 344 | "bgcolor": "#3f5159" 345 | } 346 | ], 347 | "links": [ 348 | [ 349 | 1, 350 | 4, 351 | 0, 352 | 3, 353 | 0, 354 | "MODEL" 355 | ], 356 | [ 357 | 2, 358 | 5, 359 | 0, 360 | 3, 361 | 3, 362 | "LATENT" 363 | ], 364 | [ 365 | 3, 366 | 4, 367 | 1, 368 | 6, 369 | 0, 370 | "CLIP" 371 | ], 372 | [ 373 | 4, 374 | 6, 375 | 0, 376 | 3, 377 | 1, 378 | "CONDITIONING" 379 | ], 380 | [ 381 | 5, 382 | 4, 383 | 1, 384 | 7, 385 | 0, 386 | "CLIP" 387 | ], 388 | [ 389 | 6, 390 | 7, 391 | 0, 392 | 3, 393 | 2, 394 | "CONDITIONING" 395 | ], 396 | [ 397 | 7, 398 | 3, 399 | 0, 400 | 8, 401 | 0, 402 | "LATENT" 403 | ], 404 | [ 405 | 8, 406 | 4, 407 | 2, 408 | 8, 409 | 1, 410 | "VAE" 411 | ], 412 | [ 413 | 9, 414 | 8, 415 | 0, 416 | 9, 417 | 0, 418 | "IMAGE" 419 | ], 420 | [ 421 | 14, 422 | 15, 423 | 0, 424 | 6, 425 | 1, 426 | "STRING" 427 | ] 428 | ], 429 | "groups": [], 430 | "config": {}, 431 | "extra": {}, 432 | "version": 0.4 433 | } 434 | -------------------------------------------------------------------------------- /example_workflows/magic_prompt.json: -------------------------------------------------------------------------------- 1 | { 2 | "last_node_id": 14, 3 | "last_link_id": 13, 4 | "nodes": [ 5 | { 6 | "id": 4, 7 | "type": "CheckpointLoaderSimple", 8 | "pos": [ 9 | -117, 10 | 491 11 | ], 12 | "size": { 13 | "0": 315, 14 | "1": 98 15 | }, 16 | "flags": {}, 17 | "order": 0, 18 | "mode": 0, 19 | "outputs": [ 20 | { 21 | "name": "MODEL", 22 | "type": "MODEL", 23 | "links": [ 24 | 1 25 | ], 26 | "slot_index": 0 27 | }, 28 | { 29 | "name": "CLIP", 30 | "type": "CLIP", 31 | "links": [ 32 | 3, 33 | 5 34 | ], 35 | "slot_index": 1 36 | }, 37 | { 38 | "name": "VAE", 39 | "type": "VAE", 40 | "links": [ 41 | 8 42 | ], 43 | "slot_index": 2 44 | } 45 | ], 46 | "properties": { 47 | "Node name for S&R": "CheckpointLoaderSimple" 48 | }, 49 | "widgets_values": [ 50 | "v1-5-pruned-emaonly.ckpt" 51 | ] 52 | }, 53 | { 54 | "id": 3, 55 | "type": "KSampler", 56 | "pos": [ 57 | 749, 58 | 186 59 | ], 60 | "size": { 61 | "0": 315, 62 | "1": 262 63 | }, 64 | "flags": { 65 | "collapsed": true 66 | }, 67 | "order": 5, 68 | "mode": 0, 69 | "inputs": [ 70 | { 71 | "name": "model", 72 | "type": "MODEL", 73 | "link": 1 74 | }, 75 | { 76 | "name": "positive", 77 | "type": "CONDITIONING", 78 | "link": 4 79 | }, 80 | { 81 | "name": "negative", 82 | "type": "CONDITIONING", 83 | "link": 6 84 | }, 85 | { 86 | "name": "latent_image", 87 | "type": "LATENT", 88 | "link": 2 89 | } 90 | ], 91 | "outputs": [ 92 | { 93 | "name": "LATENT", 94 | "type": "LATENT", 95 | "links": [ 96 | 7 97 | ], 98 | "slot_index": 0 99 | } 100 | ], 101 | "properties": { 102 | "Node name for S&R": "KSampler" 103 | }, 104 | "widgets_values": [ 105 | 338617970807747, 106 | "randomize", 107 | 20, 108 | 8, 109 | "euler", 110 | "normal", 111 | 1 112 | ] 113 | }, 114 | { 115 | "id": 8, 116 | "type": "VAEDecode", 117 | "pos": [ 118 | 974, 119 | 183 120 | ], 121 | "size": { 122 | "0": 210, 123 | "1": 46 124 | }, 125 | "flags": { 126 | "collapsed": true 127 | }, 128 | "order": 6, 129 | "mode": 0, 130 | "inputs": [ 131 | { 132 | "name": "samples", 133 | "type": "LATENT", 134 | "link": 7 135 | }, 136 | { 137 | "name": "vae", 138 | "type": "VAE", 139 | "link": 8 140 | } 141 | ], 142 | "outputs": [ 143 | { 144 | "name": "IMAGE", 145 | "type": "IMAGE", 146 | "links": [ 147 | 9 148 | ], 149 | "slot_index": 0 150 | } 151 | ], 152 | "properties": { 153 | "Node name for S&R": "VAEDecode" 154 | } 155 | }, 156 | { 157 | "id": 7, 158 | "type": "CLIPTextEncode", 159 | "pos": [ 160 | 503, 161 | 538 162 | ], 163 | "size": { 164 | "0": 425.27801513671875, 165 | "1": 180.6060791015625 166 | }, 167 | "flags": { 168 | "collapsed": true 169 | }, 170 | "order": 3, 171 | "mode": 0, 172 | "inputs": [ 173 | { 174 | "name": "clip", 175 | "type": "CLIP", 176 | "link": 5 177 | } 178 | ], 179 | "outputs": [ 180 | { 181 | "name": "CONDITIONING", 182 | "type": "CONDITIONING", 183 | "links": [ 184 | 6 185 | ], 186 | "slot_index": 0 187 | } 188 | ], 189 | "properties": { 190 | "Node name for S&R": "CLIPTextEncode" 191 | }, 192 | "widgets_values": [ 193 | "text, watermark" 194 | ] 195 | }, 196 | { 197 | "id": 5, 198 | "type": "EmptyLatentImage", 199 | "pos": [ 200 | 388, 201 | 191 202 | ], 203 | "size": { 204 | "0": 315, 205 | "1": 106 206 | }, 207 | "flags": { 208 | "collapsed": true 209 | }, 210 | "order": 1, 211 | "mode": 0, 212 | "outputs": [ 213 | { 214 | "name": "LATENT", 215 | "type": "LATENT", 216 | "links": [ 217 | 2 218 | ], 219 | "slot_index": 0 220 | } 221 | ], 222 | "properties": { 223 | "Node name for S&R": "EmptyLatentImage" 224 | }, 225 | "widgets_values": [ 226 | 512, 227 | 512, 228 | 1 229 | ] 230 | }, 231 | { 232 | "id": 9, 233 | "type": "SaveImage", 234 | "pos": [ 235 | 1212, 236 | 154 237 | ], 238 | "size": { 239 | "0": 210, 240 | "1": 270 241 | }, 242 | "flags": {}, 243 | "order": 7, 244 | "mode": 0, 245 | "inputs": [ 246 | { 247 | "name": "images", 248 | "type": "IMAGE", 249 | "link": 9 250 | } 251 | ], 252 | "properties": {}, 253 | "widgets_values": [ 254 | "ComfyUI" 255 | ] 256 | }, 257 | { 258 | "id": 6, 259 | "type": "CLIPTextEncode", 260 | "pos": [ 261 | 364, 262 | 77 263 | ], 264 | "size": { 265 | "0": 422.84503173828125, 266 | "1": 164.31304931640625 267 | }, 268 | "flags": { 269 | "collapsed": true 270 | }, 271 | "order": 4, 272 | "mode": 0, 273 | "inputs": [ 274 | { 275 | "name": "clip", 276 | "type": "CLIP", 277 | "link": 3 278 | }, 279 | { 280 | "name": "text", 281 | "type": "STRING", 282 | "link": 13, 283 | "widget": { 284 | "name": "text", 285 | "config": [ 286 | "STRING", 287 | { 288 | "multiline": true 289 | } 290 | ] 291 | } 292 | } 293 | ], 294 | "outputs": [ 295 | { 296 | "name": "CONDITIONING", 297 | "type": "CONDITIONING", 298 | "links": [ 299 | 4 300 | ], 301 | "slot_index": 0 302 | } 303 | ], 304 | "properties": { 305 | "Node name for S&R": "CLIPTextEncode" 306 | }, 307 | "widgets_values": [ 308 | "beautiful scenery nature glass bottle landscape, , purple galaxy bottle," 309 | ] 310 | }, 311 | { 312 | "id": 14, 313 | "type": "DPMagicPrompt", 314 | "pos": [ 315 | -271, 316 | 51 317 | ], 318 | "size": { 319 | "0": 400, 320 | "1": 200 321 | }, 322 | "flags": {}, 323 | "order": 2, 324 | "mode": 0, 325 | "outputs": [ 326 | { 327 | "name": "STRING", 328 | "type": "STRING", 329 | "links": [ 330 | 13 331 | ], 332 | "shape": 3, 333 | "slot_index": 0 334 | } 335 | ], 336 | "properties": { 337 | "Node name for S&R": "DPMagicPrompt" 338 | }, 339 | "widgets_values": [ 340 | "A mech warrior", 341 | "Yes" 342 | ], 343 | "color": "#2a363b", 344 | "bgcolor": "#3f5159" 345 | } 346 | ], 347 | "links": [ 348 | [ 349 | 1, 350 | 4, 351 | 0, 352 | 3, 353 | 0, 354 | "MODEL" 355 | ], 356 | [ 357 | 2, 358 | 5, 359 | 0, 360 | 3, 361 | 3, 362 | "LATENT" 363 | ], 364 | [ 365 | 3, 366 | 4, 367 | 1, 368 | 6, 369 | 0, 370 | "CLIP" 371 | ], 372 | [ 373 | 4, 374 | 6, 375 | 0, 376 | 3, 377 | 1, 378 | "CONDITIONING" 379 | ], 380 | [ 381 | 5, 382 | 4, 383 | 1, 384 | 7, 385 | 0, 386 | "CLIP" 387 | ], 388 | [ 389 | 6, 390 | 7, 391 | 0, 392 | 3, 393 | 2, 394 | "CONDITIONING" 395 | ], 396 | [ 397 | 7, 398 | 3, 399 | 0, 400 | 8, 401 | 0, 402 | "LATENT" 403 | ], 404 | [ 405 | 8, 406 | 4, 407 | 2, 408 | 8, 409 | 1, 410 | "VAE" 411 | ], 412 | [ 413 | 9, 414 | 8, 415 | 0, 416 | 9, 417 | 0, 418 | "IMAGE" 419 | ], 420 | [ 421 | 13, 422 | 14, 423 | 0, 424 | 6, 425 | 1, 426 | "STRING" 427 | ] 428 | ], 429 | "groups": [], 430 | "config": {}, 431 | "extra": {}, 432 | "version": 0.4 433 | } 434 | -------------------------------------------------------------------------------- /example_workflows/random.json: -------------------------------------------------------------------------------- 1 | { 2 | "last_node_id": 10, 3 | "last_link_id": 10, 4 | "nodes": [ 5 | { 6 | "id": 4, 7 | "type": "CheckpointLoaderSimple", 8 | "pos": [ 9 | -117, 10 | 491 11 | ], 12 | "size": { 13 | "0": 315, 14 | "1": 98 15 | }, 16 | "flags": {}, 17 | "order": 0, 18 | "mode": 0, 19 | "outputs": [ 20 | { 21 | "name": "MODEL", 22 | "type": "MODEL", 23 | "links": [ 24 | 1 25 | ], 26 | "slot_index": 0 27 | }, 28 | { 29 | "name": "CLIP", 30 | "type": "CLIP", 31 | "links": [ 32 | 3, 33 | 5 34 | ], 35 | "slot_index": 1 36 | }, 37 | { 38 | "name": "VAE", 39 | "type": "VAE", 40 | "links": [ 41 | 8 42 | ], 43 | "slot_index": 2 44 | } 45 | ], 46 | "properties": { 47 | "Node name for S&R": "CheckpointLoaderSimple" 48 | }, 49 | "widgets_values": [ 50 | "v1-5-pruned-emaonly.ckpt" 51 | ] 52 | }, 53 | { 54 | "id": 6, 55 | "type": "CLIPTextEncode", 56 | "pos": [ 57 | 406, 58 | 56 59 | ], 60 | "size": { 61 | "0": 422.84503173828125, 62 | "1": 164.31304931640625 63 | }, 64 | "flags": { 65 | "collapsed": true 66 | }, 67 | "order": 4, 68 | "mode": 0, 69 | "inputs": [ 70 | { 71 | "name": "clip", 72 | "type": "CLIP", 73 | "link": 3 74 | }, 75 | { 76 | "name": "text", 77 | "type": "STRING", 78 | "link": 10, 79 | "widget": { 80 | "name": "text", 81 | "config": [ 82 | "STRING", 83 | { 84 | "multiline": true 85 | } 86 | ] 87 | } 88 | } 89 | ], 90 | "outputs": [ 91 | { 92 | "name": "CONDITIONING", 93 | "type": "CONDITIONING", 94 | "links": [ 95 | 4 96 | ], 97 | "slot_index": 0 98 | } 99 | ], 100 | "properties": { 101 | "Node name for S&R": "CLIPTextEncode" 102 | }, 103 | "widgets_values": [ 104 | "beautiful scenery nature glass bottle landscape, , purple galaxy bottle," 105 | ] 106 | }, 107 | { 108 | "id": 3, 109 | "type": "KSampler", 110 | "pos": [ 111 | 749, 112 | 186 113 | ], 114 | "size": { 115 | "0": 315, 116 | "1": 262 117 | }, 118 | "flags": { 119 | "collapsed": true 120 | }, 121 | "order": 5, 122 | "mode": 0, 123 | "inputs": [ 124 | { 125 | "name": "model", 126 | "type": "MODEL", 127 | "link": 1 128 | }, 129 | { 130 | "name": "positive", 131 | "type": "CONDITIONING", 132 | "link": 4 133 | }, 134 | { 135 | "name": "negative", 136 | "type": "CONDITIONING", 137 | "link": 6 138 | }, 139 | { 140 | "name": "latent_image", 141 | "type": "LATENT", 142 | "link": 2 143 | } 144 | ], 145 | "outputs": [ 146 | { 147 | "name": "LATENT", 148 | "type": "LATENT", 149 | "links": [ 150 | 7 151 | ], 152 | "slot_index": 0 153 | } 154 | ], 155 | "properties": { 156 | "Node name for S&R": "KSampler" 157 | }, 158 | "widgets_values": [ 159 | 236449535155640, 160 | "randomize", 161 | 20, 162 | 8, 163 | "euler", 164 | "normal", 165 | 1 166 | ] 167 | }, 168 | { 169 | "id": 8, 170 | "type": "VAEDecode", 171 | "pos": [ 172 | 974, 173 | 183 174 | ], 175 | "size": { 176 | "0": 210, 177 | "1": 46 178 | }, 179 | "flags": { 180 | "collapsed": true 181 | }, 182 | "order": 6, 183 | "mode": 0, 184 | "inputs": [ 185 | { 186 | "name": "samples", 187 | "type": "LATENT", 188 | "link": 7 189 | }, 190 | { 191 | "name": "vae", 192 | "type": "VAE", 193 | "link": 8 194 | } 195 | ], 196 | "outputs": [ 197 | { 198 | "name": "IMAGE", 199 | "type": "IMAGE", 200 | "links": [ 201 | 9 202 | ], 203 | "slot_index": 0 204 | } 205 | ], 206 | "properties": { 207 | "Node name for S&R": "VAEDecode" 208 | } 209 | }, 210 | { 211 | "id": 7, 212 | "type": "CLIPTextEncode", 213 | "pos": [ 214 | 503, 215 | 538 216 | ], 217 | "size": { 218 | "0": 425.27801513671875, 219 | "1": 180.6060791015625 220 | }, 221 | "flags": { 222 | "collapsed": true 223 | }, 224 | "order": 3, 225 | "mode": 0, 226 | "inputs": [ 227 | { 228 | "name": "clip", 229 | "type": "CLIP", 230 | "link": 5 231 | } 232 | ], 233 | "outputs": [ 234 | { 235 | "name": "CONDITIONING", 236 | "type": "CONDITIONING", 237 | "links": [ 238 | 6 239 | ], 240 | "slot_index": 0 241 | } 242 | ], 243 | "properties": { 244 | "Node name for S&R": "CLIPTextEncode" 245 | }, 246 | "widgets_values": [ 247 | "text, watermark" 248 | ] 249 | }, 250 | { 251 | "id": 10, 252 | "type": "DPRandomGenerator", 253 | "pos": [ 254 | -130, 255 | 30 256 | ], 257 | "size": { 258 | "0": 400, 259 | "1": 200 260 | }, 261 | "flags": {}, 262 | "order": 1, 263 | "mode": 0, 264 | "outputs": [ 265 | { 266 | "name": "STRING", 267 | "type": "STRING", 268 | "links": [ 269 | 10 270 | ], 271 | "shape": 3, 272 | "slot_index": 0 273 | } 274 | ], 275 | "properties": { 276 | "Node name for S&R": "DPRandomGenerator" 277 | }, 278 | "widgets_values": [ 279 | "A {red|green|blue} ball by __artists/Photography/by_country/china__" 280 | ], 281 | "color": "#2a363b", 282 | "bgcolor": "#3f5159", 283 | "shape": 4 284 | }, 285 | { 286 | "id": 5, 287 | "type": "EmptyLatentImage", 288 | "pos": [ 289 | 388, 290 | 191 291 | ], 292 | "size": { 293 | "0": 315, 294 | "1": 106 295 | }, 296 | "flags": { 297 | "collapsed": true 298 | }, 299 | "order": 2, 300 | "mode": 0, 301 | "outputs": [ 302 | { 303 | "name": "LATENT", 304 | "type": "LATENT", 305 | "links": [ 306 | 2 307 | ], 308 | "slot_index": 0 309 | } 310 | ], 311 | "properties": { 312 | "Node name for S&R": "EmptyLatentImage" 313 | }, 314 | "widgets_values": [ 315 | 512, 316 | 512, 317 | 1 318 | ] 319 | }, 320 | { 321 | "id": 9, 322 | "type": "SaveImage", 323 | "pos": [ 324 | 1219, 325 | 161 326 | ], 327 | "size": { 328 | "0": 210, 329 | "1": 270 330 | }, 331 | "flags": {}, 332 | "order": 7, 333 | "mode": 0, 334 | "inputs": [ 335 | { 336 | "name": "images", 337 | "type": "IMAGE", 338 | "link": 9 339 | } 340 | ], 341 | "properties": {}, 342 | "widgets_values": [ 343 | "ComfyUI" 344 | ] 345 | } 346 | ], 347 | "links": [ 348 | [ 349 | 1, 350 | 4, 351 | 0, 352 | 3, 353 | 0, 354 | "MODEL" 355 | ], 356 | [ 357 | 2, 358 | 5, 359 | 0, 360 | 3, 361 | 3, 362 | "LATENT" 363 | ], 364 | [ 365 | 3, 366 | 4, 367 | 1, 368 | 6, 369 | 0, 370 | "CLIP" 371 | ], 372 | [ 373 | 4, 374 | 6, 375 | 0, 376 | 3, 377 | 1, 378 | "CONDITIONING" 379 | ], 380 | [ 381 | 5, 382 | 4, 383 | 1, 384 | 7, 385 | 0, 386 | "CLIP" 387 | ], 388 | [ 389 | 6, 390 | 7, 391 | 0, 392 | 3, 393 | 2, 394 | "CONDITIONING" 395 | ], 396 | [ 397 | 7, 398 | 3, 399 | 0, 400 | 8, 401 | 0, 402 | "LATENT" 403 | ], 404 | [ 405 | 8, 406 | 4, 407 | 2, 408 | 8, 409 | 1, 410 | "VAE" 411 | ], 412 | [ 413 | 9, 414 | 8, 415 | 0, 416 | 9, 417 | 0, 418 | "IMAGE" 419 | ], 420 | [ 421 | 10, 422 | 10, 423 | 0, 424 | 6, 425 | 1, 426 | "STRING" 427 | ] 428 | ], 429 | "groups": [], 430 | "config": {}, 431 | "extra": {}, 432 | "version": 0.4 433 | } 434 | -------------------------------------------------------------------------------- /example_workflows/workflow.json: -------------------------------------------------------------------------------- 1 | { 2 | "last_node_id": 10, 3 | "last_link_id": 11, 4 | "nodes": [ 5 | { 6 | "id": 8, 7 | "type": "VAEDecode", 8 | "pos": [ 9 | 1209, 10 | 188 11 | ], 12 | "size": { 13 | "0": 210, 14 | "1": 46 15 | }, 16 | "flags": {}, 17 | "order": 5, 18 | "mode": 0, 19 | "inputs": [ 20 | { 21 | "name": "samples", 22 | "type": "LATENT", 23 | "link": 7 24 | }, 25 | { 26 | "name": "vae", 27 | "type": "VAE", 28 | "link": 8 29 | } 30 | ], 31 | "outputs": [ 32 | { 33 | "name": "IMAGE", 34 | "type": "IMAGE", 35 | "links": [ 36 | 9 37 | ], 38 | "slot_index": 0 39 | } 40 | ], 41 | "properties": { 42 | "Node name for S&R": "VAEDecode" 43 | } 44 | }, 45 | { 46 | "id": 5, 47 | "type": "EmptyLatentImage", 48 | "pos": [ 49 | 439, 50 | 743 51 | ], 52 | "size": { 53 | "0": 315, 54 | "1": 106 55 | }, 56 | "flags": {}, 57 | "order": 0, 58 | "mode": 0, 59 | "outputs": [ 60 | { 61 | "name": "LATENT", 62 | "type": "LATENT", 63 | "links": [ 64 | 2 65 | ], 66 | "slot_index": 0 67 | } 68 | ], 69 | "properties": { 70 | "Node name for S&R": "EmptyLatentImage" 71 | }, 72 | "widgets_values": [ 73 | 512, 74 | 512, 75 | 1 76 | ] 77 | }, 78 | { 79 | "id": 7, 80 | "type": "CLIPTextEncode", 81 | "pos": [ 82 | 179, 83 | 431 84 | ], 85 | "size": { 86 | "0": 425.27801513671875, 87 | "1": 180.6060791015625 88 | }, 89 | "flags": {}, 90 | "order": 2, 91 | "mode": 0, 92 | "inputs": [ 93 | { 94 | "name": "clip", 95 | "type": "CLIP", 96 | "link": 5 97 | } 98 | ], 99 | "outputs": [ 100 | { 101 | "name": "CONDITIONING", 102 | "type": "CONDITIONING", 103 | "links": [ 104 | 6 105 | ], 106 | "slot_index": 0 107 | } 108 | ], 109 | "properties": { 110 | "Node name for S&R": "CLIPTextEncode" 111 | }, 112 | "widgets_values": [ 113 | "text, watermark" 114 | ] 115 | }, 116 | { 117 | "id": 3, 118 | "type": "KSampler", 119 | "pos": [ 120 | 871, 121 | 197 122 | ], 123 | "size": { 124 | "0": 315, 125 | "1": 262 126 | }, 127 | "flags": {}, 128 | "order": 4, 129 | "mode": 0, 130 | "inputs": [ 131 | { 132 | "name": "model", 133 | "type": "MODEL", 134 | "link": 1 135 | }, 136 | { 137 | "name": "positive", 138 | "type": "CONDITIONING", 139 | "link": 11 140 | }, 141 | { 142 | "name": "negative", 143 | "type": "CONDITIONING", 144 | "link": 6 145 | }, 146 | { 147 | "name": "latent_image", 148 | "type": "LATENT", 149 | "link": 2 150 | } 151 | ], 152 | "outputs": [ 153 | { 154 | "name": "LATENT", 155 | "type": "LATENT", 156 | "links": [ 157 | 7 158 | ], 159 | "slot_index": 0 160 | } 161 | ], 162 | "properties": { 163 | "Node name for S&R": "KSampler" 164 | }, 165 | "widgets_values": [ 166 | 122610977253400, 167 | "randomize", 168 | 20, 169 | 8, 170 | "euler", 171 | "normal", 172 | 1 173 | ] 174 | }, 175 | { 176 | "id": 9, 177 | "type": "SaveImage", 178 | "pos": [ 179 | 1451, 180 | 189 181 | ], 182 | "size": { 183 | "0": 210, 184 | "1": 270 185 | }, 186 | "flags": {}, 187 | "order": 6, 188 | "mode": 0, 189 | "inputs": [ 190 | { 191 | "name": "images", 192 | "type": "IMAGE", 193 | "link": 9 194 | } 195 | ], 196 | "properties": {}, 197 | "widgets_values": [ 198 | "ComfyUI" 199 | ] 200 | }, 201 | { 202 | "id": 4, 203 | "type": "CheckpointLoaderSimple", 204 | "pos": [ 205 | -385, 206 | 105 207 | ], 208 | "size": { 209 | "0": 315, 210 | "1": 98 211 | }, 212 | "flags": {}, 213 | "order": 1, 214 | "mode": 0, 215 | "outputs": [ 216 | { 217 | "name": "MODEL", 218 | "type": "MODEL", 219 | "links": [ 220 | 1 221 | ], 222 | "slot_index": 0 223 | }, 224 | { 225 | "name": "CLIP", 226 | "type": "CLIP", 227 | "links": [ 228 | 5, 229 | 10 230 | ], 231 | "slot_index": 1 232 | }, 233 | { 234 | "name": "VAE", 235 | "type": "VAE", 236 | "links": [ 237 | 8 238 | ], 239 | "slot_index": 2 240 | } 241 | ], 242 | "properties": { 243 | "Node name for S&R": "CheckpointLoaderSimple" 244 | }, 245 | "widgets_values": [ 246 | "realisticVisionV40_v40VAE.safetensors" 247 | ] 248 | }, 249 | { 250 | "id": 10, 251 | "type": "DynamicPrompts", 252 | "pos": [ 253 | 182, 254 | -92 255 | ], 256 | "size": { 257 | "0": 400, 258 | "1": 200 259 | }, 260 | "flags": {}, 261 | "order": 3, 262 | "mode": 0, 263 | "inputs": [ 264 | { 265 | "name": "clip", 266 | "type": "CLIP", 267 | "link": 10 268 | } 269 | ], 270 | "outputs": [ 271 | { 272 | "name": "CONDITIONING", 273 | "type": "CONDITIONING", 274 | "links": [ 275 | 11 276 | ], 277 | "shape": 3, 278 | "slot_index": 0 279 | } 280 | ], 281 | "properties": { 282 | "Node name for S&R": "DynamicPrompts" 283 | }, 284 | "widgets_values": [ 285 | "A {red|green|blue} ball" 286 | ] 287 | } 288 | ], 289 | "links": [ 290 | [ 291 | 1, 292 | 4, 293 | 0, 294 | 3, 295 | 0, 296 | "MODEL" 297 | ], 298 | [ 299 | 2, 300 | 5, 301 | 0, 302 | 3, 303 | 3, 304 | "LATENT" 305 | ], 306 | [ 307 | 5, 308 | 4, 309 | 1, 310 | 7, 311 | 0, 312 | "CLIP" 313 | ], 314 | [ 315 | 6, 316 | 7, 317 | 0, 318 | 3, 319 | 2, 320 | "CONDITIONING" 321 | ], 322 | [ 323 | 7, 324 | 3, 325 | 0, 326 | 8, 327 | 0, 328 | "LATENT" 329 | ], 330 | [ 331 | 8, 332 | 4, 333 | 2, 334 | 8, 335 | 1, 336 | "VAE" 337 | ], 338 | [ 339 | 9, 340 | 8, 341 | 0, 342 | 9, 343 | 0, 344 | "IMAGE" 345 | ], 346 | [ 347 | 10, 348 | 4, 349 | 1, 350 | 10, 351 | 0, 352 | "CLIP" 353 | ], 354 | [ 355 | 11, 356 | 10, 357 | 0, 358 | 3, 359 | 1, 360 | "CONDITIONING" 361 | ] 362 | ], 363 | "groups": [], 364 | "config": {}, 365 | "extra": {}, 366 | "version": 0.4 367 | } 368 | -------------------------------------------------------------------------------- /images/example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adieyal/comfyui-dynamicprompts/3f2fff32358cf39e21b8b440ca87eac9a8e2bade/images/example.png -------------------------------------------------------------------------------- /images/menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adieyal/comfyui-dynamicprompts/3f2fff32358cf39e21b8b440ca87eac9a8e2bade/images/menu.png -------------------------------------------------------------------------------- /install.py: -------------------------------------------------------------------------------- 1 | import shutil 2 | from pathlib import Path 3 | 4 | comfy_path = Path(__file__).parent.parent.parent 5 | extension_path = Path(__file__).parent 6 | js_path = comfy_path / "web" / "extensions" 7 | 8 | 9 | def copy_web_extensions(): 10 | shutil.copy(extension_path / "web-extensions/dp.js", str(js_path)) 11 | 12 | 13 | copy_web_extensions() 14 | -------------------------------------------------------------------------------- /nodes/__init__.py: -------------------------------------------------------------------------------- 1 | from .combinatorial import DPCombinatorialGenerator 2 | from .feeling_lucky import DPFeelingLucky 3 | from .jinja import DPJinja 4 | from .magicprompt import DPMagicPrompt 5 | from .output_node import OutputString 6 | from .random import DPRandomGenerator 7 | 8 | NODE_CLASS_MAPPINGS = { 9 | "DPRandomGenerator": DPRandomGenerator, 10 | "DPCombinatorialGenerator": DPCombinatorialGenerator, 11 | "DPFeelingLucky": DPFeelingLucky, 12 | "DPJinja": DPJinja, 13 | "DPMagicPrompt": DPMagicPrompt, 14 | "DPOutput": OutputString, 15 | } 16 | 17 | # A dictionary that contains the friendly/humanly readable titles for the nodes 18 | NODE_DISPLAY_NAME_MAPPINGS = { 19 | "DPRandomGenerator": "Random Prompts", 20 | "DPCombinatorialGenerator": "Combinatorial Prompts", 21 | "DPFeelingLucky": "I'm Feeling Lucky", 22 | "DPJinja": "Jinja2 Templates", 23 | "DPMagicPrompt": "Magic Prompt", 24 | "DPOutput": "OutputString", 25 | } 26 | 27 | __all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS"] 28 | -------------------------------------------------------------------------------- /nodes/combinatorial.py: -------------------------------------------------------------------------------- 1 | from functools import lru_cache 2 | 3 | from dynamicprompts.enums import SamplingMethod 4 | from dynamicprompts.sampling_context import SamplingContext 5 | 6 | from .sampler import DPAbstractSamplerNode 7 | 8 | 9 | class DPCombinatorialGenerator(DPAbstractSamplerNode): 10 | @property 11 | @lru_cache(maxsize=1) 12 | def context(self) -> SamplingContext: 13 | return SamplingContext( 14 | wildcard_manager=self._wildcard_manager, 15 | default_sampling_method=SamplingMethod.COMBINATORIAL, 16 | ) 17 | -------------------------------------------------------------------------------- /nodes/feeling_lucky.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from dynamicprompts.generators import FeelingLuckyGenerator, RandomPromptGenerator 4 | from dynamicprompts.sampling_context import SamplingContext 5 | 6 | from .sampler import DPAbstractSamplerNode 7 | 8 | logger = logging.getLogger(__name__) 9 | 10 | 11 | class DPFeelingLucky(DPAbstractSamplerNode): 12 | def __init__(self, *args, **kwargs): 13 | super().__init__(*args, **kwargs) 14 | self._random_generator = RandomPromptGenerator( 15 | wildcard_manager=self._wildcard_manager, 16 | ) 17 | self._prompt_generator = FeelingLuckyGenerator(generator=self._random_generator) 18 | 19 | def get_prompt(self, text: str, seed: int, autorefresh: str) -> tuple[str]: 20 | """ 21 | Main entrypoint for this node. 22 | Using the sampling context, generate a new prompt. 23 | """ 24 | 25 | if seed > 0: 26 | self.context.rand.seed(seed) 27 | 28 | if text.strip() == "": 29 | return ("",) 30 | 31 | try: 32 | prompt = self._prompt_generator.generate(text, 1)[0] 33 | 34 | return (str(prompt),) 35 | except Exception as e: 36 | logger.exception(e) 37 | return ("",) 38 | 39 | @property 40 | def context(self) -> SamplingContext: 41 | return self._random_generator._context 42 | -------------------------------------------------------------------------------- /nodes/generator.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | 4 | class DPGeneratorNode(ABC): 5 | RETURN_TYPES = ("STRING",) 6 | FUNCTION = "get_prompt" 7 | CATEGORY = "Dynamic Prompts" 8 | 9 | @classmethod 10 | def INPUT_TYPES(s): 11 | return { 12 | "required": { 13 | "text": ("STRING", {"multiline": True}), 14 | "autorefresh": (["Yes", "No"],), 15 | }, 16 | } 17 | 18 | @classmethod 19 | def IS_CHANGED(cls, text, autorefresh): 20 | # Force re-evaluation of the node 21 | if autorefresh == "Yes": 22 | return float("NaN") 23 | 24 | def get_prompt(self, text: str, autorefresh: str) -> tuple[str]: 25 | prompt = self.generate_prompt(text) 26 | print(f"Prompt: {prompt}") 27 | 28 | return (prompt,) 29 | 30 | @abstractmethod 31 | def generate_prompt(self, text: str) -> str: 32 | ... 33 | -------------------------------------------------------------------------------- /nodes/jinja.py: -------------------------------------------------------------------------------- 1 | from dynamicprompts.generators import JinjaGenerator 2 | 3 | from .generator import DPGeneratorNode 4 | 5 | 6 | class DPJinja(DPGeneratorNode): 7 | def generate_prompt(self, text): 8 | prompt_generator = JinjaGenerator() 9 | 10 | all_prompts = prompt_generator.generate(text, 1) or [""] 11 | return str(all_prompts[0]) 12 | -------------------------------------------------------------------------------- /nodes/magicprompt.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from dynamicprompts.generators import RandomPromptGenerator 4 | from dynamicprompts.generators.magicprompt import MagicPromptGenerator 5 | from dynamicprompts.sampling_context import SamplingContext 6 | 7 | from .sampler import DPAbstractSamplerNode 8 | 9 | logger = logging.getLogger(__name__) 10 | 11 | 12 | class DPMagicPrompt(DPAbstractSamplerNode): 13 | def __init__(self, *args, **kwargs): 14 | super().__init__(*args, **kwargs) 15 | self._random_generator = RandomPromptGenerator( 16 | wildcard_manager=self._wildcard_manager, 17 | ) 18 | self._prompt_generator = MagicPromptGenerator( 19 | prompt_generator=self._random_generator, 20 | ) 21 | 22 | def get_prompt(self, text: str, seed: int, autorefresh: str) -> tuple[str]: 23 | """ 24 | Main entrypoint for this node. 25 | Using the sampling context, generate a new prompt. 26 | """ 27 | 28 | if seed > 0: 29 | self.context.rand.seed(seed) 30 | 31 | if text.strip() == "": 32 | return ("",) 33 | 34 | try: 35 | prompt = self._prompt_generator.generate(text, 1)[0] 36 | 37 | return (str(prompt),) 38 | except Exception as e: 39 | logger.exception(e) 40 | return ("",) 41 | 42 | @property 43 | def context(self) -> SamplingContext: 44 | return self._random_generator._context 45 | -------------------------------------------------------------------------------- /nodes/output_node.py: -------------------------------------------------------------------------------- 1 | class OutputString: 2 | @classmethod 3 | def INPUT_TYPES(cls): 4 | return { 5 | "required": { 6 | "text": ("STRING", {}), 7 | }, 8 | } 9 | 10 | RETURN_TYPES = () 11 | FUNCTION = "output_string" 12 | 13 | OUTPUT_NODE = True 14 | 15 | CATEGORY = "utils" 16 | 17 | def output_string(self, string): 18 | return ({"ui": {"string": string}},) 19 | -------------------------------------------------------------------------------- /nodes/random.py: -------------------------------------------------------------------------------- 1 | from functools import lru_cache 2 | 3 | from dynamicprompts.enums import SamplingMethod 4 | from dynamicprompts.sampling_context import SamplingContext 5 | 6 | from .sampler import DPAbstractSamplerNode 7 | 8 | 9 | class DPRandomGenerator(DPAbstractSamplerNode): 10 | @property 11 | @lru_cache(maxsize=1) 12 | def context(self) -> SamplingContext: 13 | return SamplingContext( 14 | wildcard_manager=self._wildcard_manager, 15 | default_sampling_method=SamplingMethod.RANDOM, 16 | ) 17 | -------------------------------------------------------------------------------- /nodes/sampler.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import logging 4 | from abc import ABC, abstractproperty 5 | from collections.abc import Iterable 6 | from pathlib import Path 7 | 8 | from dynamicprompts.sampling_context import SamplingContext 9 | from dynamicprompts.wildcards import WildcardManager 10 | 11 | logger = logging.getLogger(__name__) 12 | 13 | 14 | class DPAbstractSamplerNode(ABC): 15 | @classmethod 16 | def INPUT_TYPES(s): 17 | return { 18 | "required": { 19 | "text": ("STRING", {"multiline": True, "dynamicPrompts": False}), 20 | "seed": ("INT", {"default": 0, "display": "number"}), 21 | "autorefresh": (["Yes", "No"], {"default": "No"}), 22 | }, 23 | } 24 | 25 | @classmethod 26 | def IS_CHANGED(cls, text: str, seed: int, autorefresh: str): 27 | # Force re-evaluation of the node 28 | return float("NaN") 29 | 30 | RETURN_TYPES = ("STRING",) 31 | FUNCTION = "get_prompt" 32 | CATEGORY = "Dynamic Prompts" 33 | 34 | def __init__(self, *args, **kwargs): 35 | super().__init__(*args, **kwargs) 36 | wildcards_folder = self._find_wildcards_folder() 37 | self._wildcard_manager = WildcardManager(path=wildcards_folder) 38 | self._current_prompt = None 39 | 40 | def _find_wildcards_folder(self) -> Path | None: 41 | """ 42 | Find the wildcards folder. 43 | First look in the comfy_dynamicprompts folder, then in the custom_nodes folder, then in the Comfui base folder. 44 | """ 45 | from folder_paths import base_path, folder_names_and_paths 46 | 47 | wildcard_path = Path(base_path) / "wildcards" 48 | 49 | if wildcard_path.exists(): 50 | return wildcard_path 51 | 52 | extension_path = ( 53 | Path(folder_names_and_paths["custom_nodes"][0][0]) 54 | / "comfyui-dynamicprompts" 55 | ) 56 | wildcard_path = extension_path / "wildcards" 57 | wildcard_path.mkdir(parents=True, exist_ok=True) 58 | 59 | return wildcard_path 60 | 61 | def _get_next_prompt(self, prompts: Iterable[str], current_prompt: str) -> str: 62 | """ 63 | Get the next prompt from the prompts generator. 64 | """ 65 | try: 66 | return next(prompts) 67 | except (StopIteration, RuntimeError): 68 | self._prompts = self.context.sample_prompts(current_prompt) 69 | try: 70 | return next(self._prompts) 71 | except StopIteration: 72 | logger.exception("No more prompts to generate!") 73 | return "" 74 | 75 | def has_prompt_changed(self, text: str) -> bool: 76 | """ 77 | Check if the prompt has changed. 78 | """ 79 | return self._current_prompt != text 80 | 81 | def get_prompt(self, text: str, seed: int, autorefresh: str) -> tuple[str]: 82 | """ 83 | Main entrypoint for this node. 84 | Using the sampling context, generate a new prompt. 85 | """ 86 | 87 | if seed > 0: 88 | self.context.rand.seed(seed) 89 | 90 | if text.strip() == "": 91 | return ("",) 92 | 93 | if self.has_prompt_changed(text): 94 | self._current_prompt = text 95 | self._prompts = self.context.sample_prompts(self._current_prompt) 96 | 97 | if self._prompts is None: 98 | logger.exception("Something went wrong. Prompts is None!") 99 | return ("",) 100 | 101 | if self._current_prompt is None: 102 | logger.exception("Something went wrong. Current prompt is None!") 103 | return ("",) 104 | 105 | new_prompt = self._get_next_prompt(self._prompts, self._current_prompt) 106 | print(f"New prompt: {new_prompt}") 107 | 108 | return (str(new_prompt),) 109 | 110 | @abstractproperty 111 | def context(self) -> SamplingContext: 112 | ... 113 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "ComfyUI - Dynamic Prompts" 3 | version = "0.2.1" 4 | dependencies = [ 5 | "dynamicprompts[attentiongrabber,magicprompt]~=0.30.2", 6 | ] 7 | 8 | [tool.ruff] 9 | target-version = "py310" 10 | select = [ 11 | "B", 12 | "C", 13 | "COM", 14 | "E", 15 | "F", 16 | "I", 17 | "UP", 18 | ] 19 | ignore = [ 20 | "C901", # Complexity 21 | "E501", # Line length 22 | "B019", # functools.lrucache 23 | ] 24 | unfixable = [ 25 | "B007", # Loop control variable not used within the loop body 26 | ] 27 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | dynamicprompts==0.30.2 2 | -------------------------------------------------------------------------------- /web-extensions/dp.js: -------------------------------------------------------------------------------- 1 | import {app} from "../scripts/app.js"; 2 | import {ComfyWidgets} from "../scripts/widgets.js"; 3 | 4 | // It is currently not possible to disable the built-in dynamic prompts-like syntax in ComfyUI. 5 | // Until that is fixed, this extension is used to disable it. 6 | const id = "DP.PromptWidget"; 7 | app.registerExtension({ 8 | name: id, 9 | addCustomNodeDefs(node_defs) { 10 | ComfyWidgets["PROMPT"] = function(node, inputName, inputData, app) { 11 | let stringWidget = ComfyWidgets["STRING"](node, inputName, inputData, app); 12 | stringWidget.widget.dynamicPrompts = false; 13 | 14 | return stringWidget; 15 | } 16 | } 17 | }); 18 | --------------------------------------------------------------------------------