diff --git a/.github/instructions/docs.instructions.md b/.github/instructions/docs.instructions.md new file mode 100644 index 0000000..6e675ce --- /dev/null +++ b/.github/instructions/docs.instructions.md @@ -0,0 +1,42 @@ +--- +description: "Use when writing or editing documentation, README files, or any markdown content." +applyTo: "**/*.md" +--- + +# Documentation Formatting + +## Software and WebUI Names + +All software names, WebUI names, and application names must be written in italic using `*name*` syntax — never plain text, bold, or code spans. + +Examples of names that must be italic: +- *ComfyUI*, *Stable Diffusion WebUI*, *Forge*, *reForge*, *SD.Next*, *InvokeAI*, *Fooocus*, *AUTOMATIC1111* +- Any other application, platform, or tool name referenced in the docs + +**Correct:** +> This extension is compatible with *ComfyUI* and *Forge*. + +**Incorrect:** +> This extension is compatible with ComfyUI and Forge. +> This extension is compatible with **ComfyUI** and `Forge`. + +**Exceptions:** + +- Names inside code blocks (` ``` ` or `` ` `` ) are exempt — they follow code formatting rules. +- When a software name is the visible text of a hyperlink (e.g., `[ComfyUI](https://...)`), plain text is acceptable. + +## Use of github alert blocks + +When adding notes, tips, warnings, or important information in the documentation, use GitHub's alert block syntax for better visibility and formatting. The syntax is as follows: +```md +> [!NOTE] +> This is a note. +> [!TIP] +> This is a tip. +> [!IMPORTANT] +> This is important information. +> [!WARNING] +> This is a warning. +> [!CAUTION] +> This is a caution. +``` diff --git a/README.md b/README.md index 27bb709..cfa8e86 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,12 @@ These are some features: * Clean up the prompt of unnecessary separators or spaces. * Combinatorial mode. -Note: when used in an *A1111* compatible webui, the extension must be loaded after any other extension that modifies the prompt (like another wildcards extension). Usually extensions load by their folder name in alphanumeric order, so if the extensions are not loading in the correct order just rename this extension's folder so the ordering works out. When in doubt, just rename this extension's folder with a "z" in front (for example) so that it is the last one to load, or manually set such folder name when installing it. +> [!NOTE] +> When used in an *A1111* compatible webui, the extension must be loaded after any other extension that modifies the prompt (like another wildcards extension). +> +> Usually extensions load by their folder name in alphanumeric order, so if the extensions are not loading in the correct order just rename this extension's folder so the ordering works out. +> +> When in doubt, just rename this extension's folder with a "z" in front (for example) so that it is the last one to load, or manually set such folder name when installing it. If the extension runs before others, like Dynamic Prompts, and the "Process wildcards" option is enabled, the wildcards will be processed by PPP and those extensions will not get them. If you disable processing the wildcards, and you intend another extension to process them, you should keep the "What to do with remaining wildcards?" option as "ignore". @@ -40,7 +45,8 @@ Notes: I also recommend a node to add LoRAs from the prompt, like: [LoRA Tag Loader for ComfyUI](https://github.com/badjeff/comfyui_lora_tag_loader) - In *SD.Next* that means only the *A1111* or *Full* parsers. It will warn you if you use the *Compel* parser. + > [!WARNING] + > In *SD.Next* that means only the *A1111* or *Full* parsers. It will warn you if you use the *Compel* parser. Does not recognize tokenizer separators like `TE2:` and `TE3:`, so sending to negative prompt from those sections of the prompt will not add them in the corresponding section of the negative prompt. diff --git a/docs/CONFIG.md b/docs/CONFIG.md index fb91de4..0881450 100644 --- a/docs/CONFIG.md +++ b/docs/CONFIG.md @@ -4,7 +4,7 @@ The extension supports a configuration file `ppp_config.yaml` with some settings that don't usually change. -By default this configuration is read from the file `ppp_config.yaml.defaults` in the extension folder. That file must not be modified. If you want to personalize any settings you should first copy it as `ppp_config.yaml` in the same folder, or in the ComfyUI user folder (preferred, but only possible in ComfyUI). The options in this new file will take precedence over those in the defaults file. +By default this configuration is read from the file `ppp_config.yaml.defaults` in the extension folder. That file must not be modified. If you want to personalize any settings you should first copy it as `ppp_config.yaml` in the same folder, or in the *ComfyUI* user folder (preferred, but only possible in *ComfyUI*). The options in this new file will take precedence over those in the defaults file. This file contains some options for how the host applications (WebUIs) should act in certain operations, and also define the supported models, including how to detect them and the model variants definitions. Host names are fixed values (those supported by the extension). The defaults file contains comments to explain the available options. @@ -25,7 +25,7 @@ The main node that processes the prompt. Inputs: * **model**: Connect here the MODEL or a string with the model class name used by *ComfyUI*. Needed for the model kind system variables. -* **modelname**: Name of the model. Needed for the detection of model variants. +* **modelname**: Filename of the model (with relative path). Needed for the detection of model variants. * **seed**: Connect here the seed used. By default it is -1 (random). * **pos_prompt**: Connect here the prompt text, or fill it as a widget. * **neg_prompt**: Connect here the negative prompt text, or fill it as a widget. @@ -45,7 +45,9 @@ Inputs: The options nodes are optional. If you don't need to change any of the default values then you don't need to use them. -The model/modelname are also optional, but if you don't set them you will not be capable of changing content based on that. You can however set them from the prompt. +The model and modelname are also optional, but if you don't set them you will not be capable of choosing content based on the model type or variant. Native model loader nodes do not output the filename, but there are custom nodes that do (like those from [ComfyUI Image Saver](https://github.com/alexopus/ComfyUI-Image-Saver)). And setting only the modelname will try to detect its class from the file contents. + +You can instead set them from the prompt and load the model afterwards. Outputs: @@ -53,13 +55,17 @@ Outputs: * **neg_prompt**: the resulting negative prompt * **variables**: the dictionary of variables set or echoed. -The outputs are lists, and in combinatorial mode there will be multiple elements that ComfyUI will process sequentially. +The outputs are lists, and in combinatorial mode there will be multiple elements that *ComfyUI* will process sequentially. ### ACB PPP Select Variable node Lets you extract the variables used from the output (or just one of them). You can use this to send only part of the prompt to, for example, a detailer node. For example: -With this prompt: `__quality__, 1girl, ${head:__eyes__, __hair__, __expression__}, __body__, __clothes__, __background__, __style__` then you extract the `head` variable and use it as prompt for the head/face detailer. +With this prompt: + +`__quality__, 1girl, ${head:__eyes__, __hair__, __expression__}, __body__, __clothes__, __background__, __style__` + +You can then extract the `head` variable and use it as prompt for the head/face detailer. Inputs: @@ -72,7 +78,7 @@ Output: ### ACB PPP Wildcards Concat node -This node lets you select up to 10 wildcards that will be concatenated with a chosen separator. You can't specify wildcard folders in the node, so use the other available options to set them. +This node lets you select up to 10 wildcards that will be concatenated with a chosen separator. You can't specify wildcard folders in the node, so use the **extra_model_paths.yaml** options (see node below) to set them. Inputs: diff --git a/docs/COOKBOOK.md b/docs/COOKBOOK.md index 05d50ff..b8d6e96 100644 --- a/docs/COOKBOOK.md +++ b/docs/COOKBOOK.md @@ -85,7 +85,7 @@ Then use `__colors_warm__` or `__colors_cold__`, or `__colors'warm'__` or `__col ## Prompt building -You can create a complex set of wildcards that build a full prompt, by using the wildcards along with variables with default values. The variables allow you to choose what parts of the prompt you want to change for specific content, or just leave the defaults. Then, inside the UI, you can use styles (with an appropiate styles node if using ComfyUI) to choose which variables to set. This makes it easy to quickly select what do you want to prompt for. +You can create a complex set of wildcards that build a full prompt, by using the wildcards along with variables with default values. The variables allow you to choose what parts of the prompt you want to change for specific content, or just leave the defaults. Then, inside the UI, you can use styles (with an appropiate styles node if using *ComfyUI*) to choose which variables to set. This makes it easy to quickly select what do you want to prompt for. Wildcards: @@ -166,7 +166,10 @@ name,prompt,negative_prompt "Character","__character__","" ``` -You first select the styles that set the variables that you are interested in changing from the default, and you end with the main wildcard that uses them to build the prompt. Note that the order of the variables don't usually matter because they are only evaluated when echoed to the prompt. This allows you to use variables inside wildcards that are inside other variables, and only at the end they will be evaluated. +You first select the styles that set the variables that you are interested in changing from the default, and you end with the main wildcard that uses them to build the prompt. + +> [!TIP] +> The order of the variables don't usually matter because they are only evaluated when echoed to the prompt. This allows you to use variables inside wildcards that are inside other variables, and only at the end they will be evaluated. Sample style selections: @@ -196,7 +199,7 @@ This would create this prompt: ## Using variables for detailer prompts -This only applies to ComfyUI, because the variables cannot be used in the ADetailer prompts of the A1111 UIs. +This only applies to *ComfyUI*, because the variables cannot be used in the ADetailer prompts of the A1111 UIs. You can put parts of the prompt to be set into variables, and then extract this variables from the output and use them in the prompts of the detailer nodes. @@ -479,7 +482,7 @@ ${quality?=!__qualities__} ## Choosing the model from the prompt -In ComfyUI, you can leave the model and modelname inputs disconnected and set the `_modelinfo` system variable (or `_modelfullname` and `_modelclass`) at the start of the prompt (maybe through a wildcard or a choice construct). Then extract the `_modelfullname` output variable using the `Select Variable` node and use its value to actually load that model. +In *ComfyUI*, you can leave the model and modelname inputs disconnected and set the `_modelinfo` system variable (or `_modelfullname` and `_modelclass`) at the start of the prompt (maybe through a wildcard or a choice construct). Then extract the `_modelfullname` output variable using the `Select Variable` node and use its value to actually load that model. You can also set and extract user variables for other ksampler inputs, like sampler, scheduler, steps, cfg and latent size. The numeric ones will need to be transformed from string to int/float using some other node. diff --git a/docs/SYNTAX.md b/docs/SYNTAX.md index d8fde5f..c7bee14 100644 --- a/docs/SYNTAX.md +++ b/docs/SYNTAX.md @@ -41,7 +41,7 @@ The construct parameters can be written with the following options (all are opti * `r`: means it allows repetition of the choices. * `o`: means it is optional, and no error will be raised if there are no choices to select from. * `n` or `n-m` or `n-` or `-m`: number or range of choices to select. Allows zero as the start of a range. Default is 1. -* `'description'`: optional description (quoted), only valid in wildcard definitions. Used only in the Wildcards Concat node in ComfyUI. +* `'description'`: optional description (quoted), only valid in wildcard definitions. Used only in the Wildcards Concat node in *ComfyUI*. * `$$sep`: separator when multiple choices are selected. Default is set in settings. * `$$`: end of the parameters (not optional if any parameters). @@ -71,10 +71,10 @@ These are examples of formats you can use to insert a choice construct: | `{o$$if _is_sd1::choice1\|if _is_sd2::choice2}` | select 1 choice, both have conditions, if none matches it is allowed because we indicate that it is optional | | `{choice1\|choice2\|%0.5::include path/wildcard}` | select 1 choice from the two specified and the ones inside the path/wildcard wildcard, which will be weighted with half their weights | -Notes: - -* The *Dynamic Prompts* format `{2$$__flavours__}` does not work as expected because the wildcard is considered only one possible choice (it will only output one value). You can write it instead as a wildcard with parameters `__2$$flavours__`. -* Whitespace around the choices is not ignored like in *Dynamic Prompts*, but will be cleaned up if the appropriate cleaning settings are selected. +> [!NOTE] +> The *Dynamic Prompts* format `{2$$__flavours__}` does not work as expected because the wildcard is considered only one possible choice (it will only output one value). You can write it instead as a wildcard with parameters `__2$$flavours__`. +> +> Whitespace around the choices is not ignored like in *Dynamic Prompts*, but will be cleaned up if the appropriate cleaning settings are selected. ## Wildcards @@ -82,7 +82,8 @@ The generic format is: `__parameters$$wildcard'filter'(var=value)__` The parameters, the filter, and the setting of a variable are optional. The parameters follow the same format as for the choices. -Wildcards cannot be used inside an extranetwork tag (because some LoRA names contain double underscores). If you need to choose from multiple LoRAs put the whole extranetwork tag inside a wildcard, or use choices. +> [!WARNING] +> Wildcards cannot be used inside an extranetwork tag (because some LoRA names contain double underscores). If you need to choose from multiple LoRAs put the whole extranetwork tag inside a wildcard, or use choices. ### Identifier @@ -174,13 +175,15 @@ A choice inside a wildcard can also be a list or a dictionary of one element con Remember you can use the include command on choices to compose a wildcard from other wildcards' choices. -Note: the files should have UTF-8 encoding. The extension will also try with windows-1252 if that fails. +> [!IMPORTANT] +> The files should have UTF-8 encoding. The extension will also try with windows-1252 if that fails. Wildcard definitions are reloaded automatically on each generation if they change. ### Detection of remaining wildcards -This extension should run after any other wildcard extensions, so if you don't use the internal wildcards processing, any remaining wildcards present in the prompt or negative_prompt at this point must be invalid. Usually you might not notice this problem until you check the image metadata, so this option gives you some ways to detect and treat the problem. +> [!IMPORTANT] +> This extension should run after any other wildcard extensions, so if you don't use the internal wildcards processing, any remaining wildcards present in the prompt or negative_prompt at this point must be invalid. Usually you might not notice this problem until you check the image metadata, so this option gives you some ways to detect and treat the problem. ## Set Wildcard Default Filter command @@ -205,26 +208,25 @@ All these variables can be used to output content or behave differently based on Names starting with an underscore are reserved for system variables: -| System variable | Value | -| --------------- | ----- | -| `_model` | the loaded model identifier (`sd1`, `sd2`, `sdxl`, `sd3`, `flux`, `auraflow`). `_sd` also works but is deprecated. | -| `_modelname` | the loaded model filename (without path). `_sdname` also works but is deprecated. | -| `_modelfullname` | the loaded model filename (with path). `_sdfullname` also works but is deprecated. In ComfyUI this variable can also be **set** to override the filename used for model detection (see below). | -| `_modelclass` | the class used for the model. Note that this is dependent on the webui. In A1111 all SD versions use the same class. Can be used for new models that are not supported yet with the `_is_*` variables. The debug setting will show all system variables when generating in case you need to see which one to use for a certain model. In ComfyUI this variable can also be **set** to override the class used for model detection (see below). | -| `_modelinfo` | Contains both class and model full name separated with `@`. In ComfyUI can be set to change both `_modelfullname` and `_modelclass` at once (see below). | -| `_is_sd` | true if the loaded model version is any version of SD | -| `_is_sd1` | true if the loaded model version is SD 1.x | -| `_is_sd2` | true if the loaded model version is SD 2.x | -| `_is_sdxl` | true if the loaded model version is SDXL (includes Pony models) | -| `_is_sd3` | true if the loaded model version is SD 3.x | -| `_is_flux` | true if the loaded model is Flux | -| `_is_auraflow` | true if the loaded model is AuraFlow | -| `_is_ssd` | true if the loaded model version is SSD (Segmind Stable Diffusion 1B). Note that for an SSD model `_is_sdxl` will also be true. | -| `_is_sdxl_no_ssd` | true if the loaded model version is SDXL and not an SSD model. | -| `_is_sdxl_no_pony` | true if the loaded model version is SDXL and not a Pony model (the `pony` variant must be defined in settings). Kept to maintain compatibility with previous versions. | -| `_is_vvvv` | true if the loaded model matches the *vvvv* model variant definition (based on its filename). Note that the corresponding variable for the model kind will also be true. | -| `_is_pure_kkkk` | true if the loaded model is of kind *kkkk* (f.e. sdxl) and not a variant. | -| `_is_variant_kkkk` | true if the loaded model version is any variant of model kind *kkkk* and not the pure version. Note that the corresponding variable for the model kind will also be true. | +| System variable | Value | +| --------------- | ----- | +| `_model` | the model identifier (`sd1`, `sd2`, `sdxl`, `sd3`, `flux`, `auraflow`). `_sd` also works but is deprecated. | +| `_modelname` | the model filename (without path). `_sdname` also works but is deprecated. | +| `_modelfullname` | the model filename (with path). `_sdfullname` also works but is deprecated. In *ComfyUI* this variable can also be **set** to override the filename used for model detection (see below). | +| `_modelclass` | the class used for the model. Note that this is dependent on the webui. In A1111 all SD versions use the same class. Can be used for new models that are not supported yet with the `_is_*` variables. The debug setting will show all system variables when generating in case you need to see which one to use for a certain model. In *ComfyUI* this variable can also be **set** to override the class used for model detection (see below). | +| `_modelinfo` | Contains both class and model full name separated with `@`. In *ComfyUI* can be set to change both `_modelfullname` and `_modelclass` at once (see below). | +| `_is_kkkk` | true if the model is of kind *kkkk* (the model identifier, f.e. sdxl; those set in the ppp_config.yaml file) | +| `_is_vvvv` | true if the model matches the *vvvv* model variant definition (based on its filename). Note that the corresponding variable for the model kind will also be true. | +| `_is_pure_kkkk` | true if the model is of kind *kkkk* and not a variant. | +| `_is_variant_kkkk` | true if the model version is any variant of model kind *kkkk* and not the pure version. Note that the corresponding variable for the model kind will also be true. | +| `_is_sd` | true if the model is any version of SD | +| `_is_ssd` | true if the model is SSD (Segmind Stable Diffusion 1B). Note that for an SSD model `_is_sdxl` will also be true. | +| `_is_sdxl_no_ssd` | true if the model is SDXL and not an SSD model. | +| `_is_sdxl_no_pony` | true if the model is SDXL and not a Pony model (the `pony` variant must be defined in settings). Kept to maintain compatibility with previous versions. | +| `_opt_...` | All the options. | + +> [!NOTE] +> The model path is relative to the checkpoint/difussion_models folder, just as it appears in the load nodes. ## Set command @@ -262,14 +264,19 @@ System variables cannot be set, except in some cases (see next point). ### Overriding model information from the prompt (ComfyUI only) -In ComfyUI, `_modelfullname`, `_modelclass`, and `_modelinfo` can be **set** from the prompt using the `set` command or `${}` syntax. Setting any of them triggers a re-evaluation of all `_is_*` system variables, so conditions that come later in the prompt will reflect the new values. +In *ComfyUI*, `_modelfullname`, `_modelclass`, and `_modelinfo` can be **set** from the prompt using the `set` command or `${}` syntax. Setting any of them triggers a re-evaluation of all `_is_*` system variables, so conditions that come later in the prompt will reflect the new values. -This is useful in workflows where the model information is not passed through the standard ComfyUI model input but is known at prompt time. And the `_modelfullname` variable can be extracted later by using the Select Variable node to actually load that model. +This is useful in workflows where the model information is not passed through the standard *ComfyUI* model input but is known at prompt time. And the `_modelfullname` variable can be extracted later by using the Select Variable node to actually load that model. `_modelinfo` is a shorthand that sets both `_modelfullname` and `_modelclass` in a single call. Its value must use the format `@`: +If a class is not specified (either in the `_modelinfo` or in `_modelclass`) but a the modelname is, it will try to detect the class from the file. + +Model filename is relative to the specific model folder (checkpoints, diffusion_models). + ```text ${_modelinfo=Flux@fluxmodel.safetensors} +${_modelinfo=@fluxmodel.safetensors} ``` Or individually: @@ -278,7 +285,8 @@ Or individually: ${_modelclass=Flux}${_modelfullname=fluxmodel.safetensors} ``` -Attempting to set these variables in any other host will trigger a warning or error depending on the **What to do on invalid content warnings?** setting. +> [!WARNING] +> Attempting to set these variables in any other host will trigger a warning or error depending on the **What to do on invalid content warnings?** setting. ## Echo command diff --git a/ppp.py b/ppp.py index 638d3a0..9912de1 100644 --- a/ppp.py +++ b/ppp.py @@ -27,7 +27,7 @@ from ppp_variables import VariableRepository from ppp_logging import DEBUG_LEVEL, log from ppp_tree import TreeProcessor from ppp_utils import escape_single_quotes -from ppp_common import load_grammar, parse_prompt, preprocess_grammar, warn_or_stop +from ppp_common import get_model_class_from_filename, load_grammar, parse_prompt, preprocess_grammar, warn_or_stop from ppp_wildcards import PPPWildcards from ppp_enmappings import PPPExtraNetworkMappings @@ -369,6 +369,12 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in """Updates the is_* model detection flags in env_info based on model_class.""" prop_base = env_info.get("property_base", None) model_class = env_info.get("model_class", "") + model_name = env_info.get("model_filename", "") + if not model_class and model_name and env_info.get("app", "") == SUPPORTED_APPS.comfyui.value: + model_class = get_model_class_from_filename(model_name) + if model_class: + env_info["model_class"] = model_class + self.log(logging.DEBUG, f"Detected model class '{model_class}' from filename '{model_name}'", min_level=DEBUG_LEVEL.minimal) app = env_info.get("app", "") for m in self.known_models: env_info["is_" + m] = False @@ -377,7 +383,7 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in model_detect_for_app: ModelDetectConfig | None = model_detect.get(app) if model_detect_for_app is not None: cls_list = model_detect_for_app.class_ or [] - if model_class in cls_list: + if model_class and model_class in cls_list: env_info["is_" + m] = True elif model_detect_for_app.property is not None and prop_base is not None: prop = model_detect_for_app.property diff --git a/ppp_comfyui.py b/ppp_comfyui.py index 8443167..39636de 100644 --- a/ppp_comfyui.py +++ b/ppp_comfyui.py @@ -7,6 +7,7 @@ import nodes # type: ignore from ppp import PromptPostProcessor from ppp_classes import IFWILDCARDS_CHOICES, ONWARNING_CHOICES, SUPPORTED_APPS, PPPStateOptions +from ppp_common import get_model_class_from_filename from ppp_logging import DEBUG_LEVEL, PromptPostProcessorLogFactory, log from ppp_utils import escape_single_quotes from ppp_wildcards import PPPWildcards @@ -310,6 +311,15 @@ class PromptPostProcessorComfyUINode: modelclass = ( model.model.model_config.__class__.__name__ if model is not None and not isinstance(model, str) else model ) or "" + if modelclass == "": + modelclass = get_model_class_from_filename(modelname) + if modelclass: + log( + self.logger, + DEBUG_LEVEL.minimal, + logging.DEBUG, + f"Detected model class '{modelclass}' from filename '{modelname}'", + ) if modelclass == "": log( self.logger, diff --git a/ppp_common.py b/ppp_common.py index b1bcb47..96e227f 100644 --- a/ppp_common.py +++ b/ppp_common.py @@ -196,3 +196,41 @@ def preprocess_grammar(grammar_content: str, options: dict[str, bool], logger: l f"Found {len(skip_current_block)} unclosed conditional directive(s) at the end of the grammar file" ) return "\n".join(result_lines) + +def get_model_class_from_filename(filename: str) -> str: + try: + import folder_paths # type: ignore + import comfy.utils # type: ignore + import comfy.model_detection as model_detection # type: ignore + except ImportError: + return "" + import json + + if not filename: + return "" + full_path = folder_paths.get_full_path("checkpoints", filename) + if not full_path: + full_path = folder_paths.get_full_path("diffusion_models", filename) + if not full_path or not full_path.lower().endswith((".safetensors", ".sft")): + return "" + try: + header_bytes = comfy.utils.safetensors_header(full_path) + if header_bytes is None: + return "" + header = json.loads(header_bytes) + + # Build a mock state dict — detection only needs key names and shapes + class _ShapeProxy: + def __init__(self, shape): + self.shape = shape + + def __getitem__(self, i): + return self.shape[i] # shape[i] access + + mock_sd = {k: _ShapeProxy(v["shape"]) for k, v in header.items() if k != "__metadata__" and "shape" in v} + + prefix = model_detection.unet_prefix_from_state_dict(mock_sd) + config = model_detection.model_config_from_unet(mock_sd, prefix) + return config.__class__.__name__ if config else "" + except Exception: # pylint: disable=broad-except + return "" diff --git a/web/docs/ACBPPPCleanupOptions.md b/web/docs/ACBPPPCleanupOptions.md index 1138975..9af6e12 100644 --- a/web/docs/ACBPPPCleanupOptions.md +++ b/web/docs/ACBPPPCleanupOptions.md @@ -17,7 +17,8 @@ Provides clean up options to the main PPP node. * **merge_attention**: It merges attention modifiers when possible (merges into one, multiplying their values). Only merges individually nested modifiers. * **remove_extranetwork_tags**: Removes all extra network tags. -Please note that *ComfyUI* does not natively support the `BREAK` and `AND` constructs, but the related settings are kept in that UI in case you use a node that supports them and the extension is configured to allow them (see the configuration file below). +> [!NOTE] +> *ComfyUI* does not natively support the `BREAK` and `AND` constructs, but the related settings are kept in that UI in case you use a node that supports them and the extension is configured to allow them (see the configuration file below). ## Outputs