From 1cf88ca313378e466a7a127166bb2c531ef283f2 Mon Sep 17 00:00:00 2001 From: Legende <68666984+xLegende@users.noreply.github.com> Date: Sun, 13 Apr 2025 09:32:07 +0200 Subject: [PATCH] Update README.md --- README.md | 213 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 151 insertions(+), 62 deletions(-) diff --git a/README.md b/README.md index 6b34c54..3665f11 100644 --- a/README.md +++ b/README.md @@ -1,83 +1,172 @@ -# ComfyUI - Categorized Prompt Formatter Node +# 📝 ComfyUI Prompt Formatting Nodes -A custom node for ComfyUI that allows you to filter and restructure text prompts based on tag categories defined in a YAML file. +This repository contains custom nodes for ComfyUI designed to help structure, filter, and generate text prompts using categorized tag definitions stored in a YAML file. -This node takes a standard comma-separated prompt, categorizes each tag according to your definitions, and then rebuilds the prompt based on a template you provide. It's useful for enforcing prompt structure, extracting specific concepts, or reordering elements dynamically. +## Nodes Included -![Example Node UI](custom_node.png) +1. **📝 Categorized Prompt Formatter:** Filters and restructures an *existing* input prompt based on categories and a template. Preserves tag details like weights. +2. **📝 Categorized Random Prompt Formatter:** *Generates* a new random prompt by sampling tags from categories defined in the YAML, using a template structure and a seed for reproducibility. -## Features +--- -* **YAML-based Categorization:** Define which tags belong to which categories in a simple `.yaml` file. -* **Multi-Category Tags:** A single tag (e.g., "blue eyes") can belong to multiple categories (e.g., `eyes`, `person_details`). -* **Template-Driven Output:** Structure your output prompt using placeholders like `<|category_name|>`. -* **Flexible Delimiters:** Configure input and output delimiters. -* **Weight/Emphasis Handling:** Attempts to preserve common syntax like `(tag:1.2)` and `[tag]` while using the base tag for categorization. -* **Underscore/Space Matching:** Optionally treat tags like `red_eyes` and `red eyes` as equivalent during matching (while preserving original format in output). -* **Duplicate Prevention:** Optionally prevent the same tag from appearing multiple times in the output, even if referenced by multiple categories in the template (first occurrence wins). -* **Case Sensitivity Option:** Choose whether matching should be case-sensitive or not. -* **Unmatched Tag Handling:** Decide whether to discard tags not used in the template, append them to the end, or output them separately. +## Key Features (Overall) + +* **YAML-based Categorization:** Define tags belonging to specific categories (e.g., `eyes`, `style`, `quality`) in an easy-to-edit YAML file. +* **Hierarchical Categories (`$include`):** Define broad categories that automatically inherit tags from more specific ones without manual repetition in the YAML. +* **Template-Driven Output:** Control the structure of the output prompt using simple placeholders like `<|category_name|>`. +* **Flexible Tag Handling:** Options for case sensitivity and matching tags regardless of underscores vs. spaces (e.g., `blue_eyes` vs `blue eyes`). + +--- ## Installation -1. Clone this repository into your `ComfyUI/custom_nodes/` directory: +1. **Clone the Repository:** + Navigate to your ComfyUI `custom_nodes` directory and clone this repository: ```bash cd ComfyUI/custom_nodes/ git clone https://github.com/xLegende/ComfyUI-Prompt-Formatter.git ``` - -2. Install dependencies: - * Navigate to the node's directory: `cd ComfyUI-Prompt-Formatter` - * Run the install script using your ComfyUI Python environment: - ```bash - # Example using a virtual environment: - # path/to/your/ComfyUI/python_env/bin/python install.py - # Or if your system python is the one used by ComfyUI: - python install.py - ``` - * This will install the `PyYAML` library if it's not already present. -3. Restart ComfyUI. -The node "📝 Categorized Prompt Formatter" should now appear under the "text/filtering" category when you right-click or double-click on the ComfyUI canvas. +2. **Install Dependencies:** + This node requires the `PyYAML` library. Navigate into the cloned directory and run the installation script using the Python environment associated with your ComfyUI installation: + ```bash + cd ComfyUI-Prompt-Formatter + # Example (adjust path to your Python executable if needed): + # /path/to/your/ComfyUI/python_env/bin/python install.py + # Or if your system python is the one used by ComfyUI: + python install.py + ``` -## Usage +3. **Restart ComfyUI:** + Ensure ComfyUI is completely restarted to recognize the new nodes. -1. **Prepare your YAML file:** - * Create a `.yaml` file (e.g., `my_categories.yaml`) or modify the included `prompt_categories.yaml`. - * The format is: - ```yaml - category_name_1: - - tag1 - - tag2 - - tag_shared - category_name_2: - - tag3 - - tag_shared # This tag now belongs to both categories - - "(tag with weight:1.1)" # Define tags exactly as they might appear - # ... etc - ``` - * Place this file somewhere ComfyUI can find it (e.g., in the `ComfyUI/input/` directory, or provide an absolute path, or place it in the `custom_nodes/ComfyUI-Prompt-Formatter/` folder). -2. **Add the Node:** Add the "📝 Categorized Prompt Formatter" node to your workflow. -3. **Connect Input:** Connect your source prompt string to the `prompt` input. -4. **Configure Widgets:** - * `category_definition_file`: Enter the name (e.g., `my_categories.yaml`) or full path to your YAML file. The node will search relative to itself, in `ComfyUI/input/`, and absolute paths. - * `output_template`: Define your desired output structure using `<|category_name|>` placeholders. Example: `<|quality|>, <|character_num|>, <|eyes|>, <|clothing|>, <|setting|>` - * Adjust delimiters, case sensitivity, weight handling, and unmatched tag handling as needed. - * `match_underscores_spaces` (Boolean, default: True): If enabled, the node will try to match input tags against YAML definitions by checking variants with underscores swapped for spaces (e.g., input `red_eyes` can match YAML `red eyes`, and vice-versa). The original format of the input tag is kept for the output string. -5. **Connect Output:** Connect the `formatted_prompt` output to the next node in your workflow (e.g., a CLIP Text Encode node). Use the optional `rejected_prompt` output for debugging or alternative workflows. +--- -## Example +## The YAML Definitions File (`prompt_categories.yaml`) -* **Input Prompt:** `"masterpiece, 1girl, (red eyes:1.1), long hair, wearing a blue dress, outdoors, detailed background"` -* **YAML (`prompt_categories.yaml` from this repo):** (Contains categories like `quality`, `character_num`, `eyes`, `hair_style`, `person_details`, `clothing`, `setting`) -* **Output Template:** `<|quality|>, <|character_num|>, <|eyes|>, <|clothing|>, <|setting|>` +Both nodes rely on a YAML file to define which tags belong to which categories. An example file (`prompt_categories.yaml`) is included in this repository. + +**Format:** + +* **Basic:** + ```yaml + category_name: + - tag1 + - tag2 + - another tag + ``` +* **Hierarchical Inclusion (`$include`):** A category can include all tags from other categories using the `$include` directive. It can also have its own specific tags listed under the `tags:` key. + ```yaml + eyes: + - blue eyes + - red eyes + + person_details: + $include: # Inherit from 'eyes' + - eyes + tags: # Add tags specific to 'person_details' + - smiling + - freckles + + # Using '<|person_details|>' will now consider tags from 'eyes' AND 'smiling', 'freckles'. + ``` + +**File Location:** + +The nodes will search for the specified YAML file in this order: +1. Absolute path (if provided). +2. Relative to the `ComfyUI-Prompt-Formatter` node directory. +3. Relative to the main ComfyUI `input/` directory. + +**Example File:** See the included [prompt_categories.yaml](prompt_categories.yaml) for a comprehensive example. + +--- + +## 1. 📝 Categorized Prompt Formatter + +This node takes an existing prompt string, analyzes its tags against your YAML definitions, and reconstructs a new prompt based on your template, applying various filtering options. + +![Categorized Prompt Formatter Node UI](formatter_node.png) + +### Inputs & Widgets + +* `prompt` (String): The input prompt string (e.g., "1girl, masterpiece, blue_eyes, smiling..."). +* `category_definition_file` (String): Name or path to your category YAML file (e.g., "prompt_categories.yaml"). +* `output_template` (String): Defines the output structure using placeholders. + * `<|category_name|>`: Includes all matched tags for this category. + * `<|category_name:N|>`: + * If N > 0: Includes **at most** the **first N** matched tags. + * If N < 0: Includes **at most** the **last `abs(N)`** matched tags. + * If N = 0: Includes **no** tags for this category. +* `input_delimiter` (String): Character(s) separating tags in the input `prompt` (Default: `,`). +* `output_delimiter` (String): Character(s) used to join tags in the output `formatted_prompt` (Default: `, `). +* `strip_whitespace` (Boolean): Remove whitespace around input tags before matching (Default: True). +* `case_sensitive_matching` (Boolean): Match tags case-sensitively against YAML (Default: False). +* `handle_weights` (Boolean): Parse and preserve weights like `(tag:1.2)` or `[tag]` (Default: True). +* `match_underscores_spaces` (Boolean): Treat `tag_one` and `tag one` as equivalent during matching (Default: True). Output preserves original format. +* `disable_duplicates` (Boolean): Prevent the same tag string from appearing multiple times in the output (first occurrence wins) (Default: False). +* `unmatched_tag_handling` (Dropdown): What to do with input tags not used by the template: + * `discard`: Ignore them (Default). + * `append_end`: Add them all to the end of the formatted prompt. + * `output_separately`: Output them in the `rejected_prompt` slot. + +### Outputs + +* `formatted_prompt` (String): The restructured prompt based on the template. +* `rejected_prompt` (String): Tags not included in `formatted_prompt` (content depends on `unmatched_tag_handling`). + +### Example Usage + +* **Input Prompt:** `"masterpiece, 1girl, blue eyes, red eyes, smiling, detailed background, anime style, illustration"` +* **YAML:** (Assumes `quality`, `character_num`, `eyes`, `person_details`, `style` categories exist) +* **Template:** `<|quality:1|>, <|character_num|>, <|eyes:-1|>, <|style:1|>` * **Unmatched Handling:** `output_separately` +* **Disable Duplicates:** `False` -* **Result (`formatted_prompt`):** `"masterpiece, 1girl, (red eyes:1.1), wearing a blue dress, outdoors"` -* **Result (`rejected_prompt`):** `"long hair, detailed background"` (Because `<|hair_style|>` wasn't in the template, and "detailed background" might not be defined or its category wasn't used) +* **Result (`formatted_prompt`):** `"masterpiece, 1girl, red eyes, anime style"` +* **Result (`rejected_prompt`):** `"blue eyes, smiling, detailed background, illustration"` (Assuming 'smiling', 'detailed background' weren't matched or used; 'blue eyes' was dropped by the limit `-1`; 'illustration' dropped by limit `1`) -## Notes +--- -* The YAML file path finding tries to be robust, checking relative to the node, the ComfyUI `input` directory, and absolute paths. If it can't find the file, it will print a warning and proceed without categorization. -* Weight handling uses regular expressions to parse common patterns. Very complex nested structures might not parse correctly. The base tag extracted is used for lookup. -* If a tag belongs to multiple categories, and the template requests those multiple categories, the tag **will appear multiple times** in the output string by default. \ No newline at end of file +## 2. 📝 Categorized Random Prompt Formatter + +This node generates completely new, random prompts by selecting tags from your YAML categories based on a template structure and a seed. + +![Categorized Random Prompt Formatter Node UI](random_node.png) + +### Inputs & Widgets + +* `category_definition_file` (String): Name or path to your category YAML file. +* `output_template` (String): Defines the structure and *how many* random tags to pick per category. + * `<|category_name|>`: Picks **1** random tag from the category. + * `<|category_name:N|>`: Picks **N** unique random tags from the category (N >= 0). If fewer than N tags exist, picks all available. N=0 picks none. +* `seed` (Int): Seed for the random number generator. 0 generates a new random seed each run; any other value provides reproducible results. +* `output_delimiter` (String): Character(s) used to join the selected random tags (Default: `, `). + +### Outputs + +* `random_prompt` (String): The generated random prompt string. +* `used_seed` (Int): The actual seed used for generation (useful if input `seed` was 0). + +### Example Usage + +* **YAML:** (Assumes standard categories exist) +* **Template:** `<|quality:1|>, <|character_num:1|>, <|hair_style:2|>, <|setting:1|>` +* **Seed:** `42` + +* **Possible Result (`random_prompt`):** `"best quality, 1girl, long hair, twintails, outdoors"` (Specific tags depend on seed 42 and YAML content) +* **Result (`used_seed`):** `42` + +* **If Seed = -1:** +* **Possible Result (`random_prompt`):** `"masterpiece, 2boys, short hair, ponytail, indoors"` (Different random result) +* **Result (`used_seed`):** (A large random integer) + +--- + +## Notes & Troubleshooting + +* **YAML Not Found:** If the node prints a warning about not finding the YAML file, double-check the filename/path and ensure it's accessible (in the node folder, `ComfyUI/input/`, or an absolute path). +* **Restart Required:** Always restart ComfyUI after installing or updating custom nodes. +* **`$include` Cycles:** The code has basic protection against circular `$include` directives in the YAML (e.g., A includes B, B includes A). If detected, a warning is printed, and the cycle is broken. +* **Tag Order (Formatter):** When using limits (`<|cat:N|>`) or duplicate prevention in the Formatter node, the order of tags *in the input prompt* determines which ones are selected first or last. +* **Tag Format Preservation (Formatter):** The Formatter node preserves the exact format (including weights, underscores/spaces) of the *input tags* when placing them in the output, even if matching used variants (lowercase, space/underscore swap).