├── .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 |
--------------------------------------------------------------------------------