Profile Tools (Split / Replace by Profile)
Split your prompt by category instead of doing tag surgery by hand
- kept
- rest
Somewhere in every long prompt there's a block you actually care about and a block you inherited. The lighting tags you copied from an example. The clothing tags a character LoRA wants. The masterpiece, best quality boilerplate nobody dares delete. After enough of that, "just edit the text" stops scaling, and you want a node that hands you only the clothing tags - or everything except them.
That's Profile Tools. It takes a prompt, checks each tag against saved tag lists, and returns two strings: the tags that matched, and the rest.
What it's actually for
Splitting a prompt into reusable blocks. If kept always comes out as your character description and rest as the scene, save the character half and reuse it for the next five workflows.
Swapping a category wholesale. replace pulls every profile tag out of the prompt and drops your replacement tags in place - "red dress" to "blue dress" without touching the lighting or pose.
One thing it's not for: wiring rest into your negative so the model "avoids" what you left out. A negative prompt isn't a filter, it's the unconditional branch of guidance, and at CFG 1 ComfyUI skips it outright (comfy/samplers.py gates it on math.isclose(cond_scale, 1.0)). You'd be feeding text nothing reads.
How the matching works, because it's stricter than you think
Tags split on commas or newlines, then get canonicalised: brackets stripped, (x:1.2) weights stripped, case folded, spaces and underscores treated as the same character. So (Blue_Shirt:1.3) matches a list entry of blue shirt.
The part that bites: matching compares the whole tag to list entries - equality, not substring. light blue shirt won't match a profile containing blue shirt. If your lists are broad categories and your prompts are specific phrases, that's most of your "it didn't match anything."
Built-in profiles come from .txt files, not code. First run creates tag_lists/ (editable copies) next to the shipped tag_lists_default/, and each file becomes one profile - face.txt → ★ Face. The pos/neg labels are filing, not behaviour; a ★ list appears under both. Drop animals.txt in that folder and ★ Animals exists. Edits apply with no restart: IS_CHANGED hashes the profile contents and each list file's mtime.
The inputs that matter
text is the prompt. operation switches between split and replace, and changes what the outputs mean - see the trap below. profile is required; profile_2 through profile_4 let you combine up to four lists.
mode decides how they combine: any_profile unions (match if the tag is in at least one list), all_profiles intersects (only tags in every list). Stacking ★ Face and ★ Hair with all_profiles returns nothing, because no tag is both.
In split mode, add_tags and remove_tags override per tag: the first forces tags into output one, the second forces them out, and remove wins. Those are the +/- buttons on each chip - the buttons just write into the widgets. In replace mode, replacement is what goes in, insert_at is where (where_first_match, start, end), and if_no_match decides whether a no-hit prompt gets the replacement appended, prepended, or left alone.
Both outputs are STRING: kept and rest in split mode - feed them into two CLIP Text Encode nodes. In replace mode the same sockets change job: output one is the rewritten prompt, output two the removed tags. Beginners wire these backwards about once per install.
separator defaults to , and only affects how the output is joined, not how your input splits. A prompt that uses | or BREAK as separators won't segment the way you expect.
Install
Zero dependencies: no requirements.txt, no pip step, no model downloads - stdlib Python plus aiohttp, which ComfyUI already serves on.
cd ComfyUI/custom_nodes
git clone https://github.com/mohsensd1373/smart_word_filter
Restart ComfyUI, then hard-refresh the browser (Ctrl+F5) so the frontend extension loads. ComfyUI Manager should find it by pack name - it's on the Comfy Registry under publisher kingblack7313.
Where people get burned
"Select a saved profile (create one in the Smart Word Filter node, then refresh ComfyUI)." That red validation error shows up on a fresh install. The ★ lists count as profiles, so pick one; with nothing saved the dropdown reads (no profile saved).
A profile you just saved isn't in the dropdown. Choices are built at node creation, so a loaded graph doesn't know about new profiles. Refresh the page or add a fresh node. Existing profiles' contents do update live - it's the list itself that's stale.
Deleting a built-in file doesn't delete the profile - tag_lists/face.txt is regenerated from the shipped defaults next run. To narrow a ★ list, edit it, don't remove it.
add_tags entries arrive at the end of kept, even ones absent from the prompt - occasionally that shifts attribute binding in multi-subject prompts.
If all you need is one prompt in, one prompt out, skip this - Smart Word Filter (Pos/Neg) in the same pack is the workhorse.
Inputs (13)
| Name | Type | Default | Description |
|---|---|---|---|
| text | STRING | — | |
| operation | COMBO | split: keep profile tags in output 1, the rest in output 2. | replace: remove profile tags and insert the 'replacement' tags. | |
| profile | COMBO | 24 options: pos | ★ Background, pos | ★ Body, pos | ★ Body Pose, pos | ★ Camera Style, pos | ★ Character Type, pos | ★ Clothing, +18 | |
| separator | STRING | , | — |
| mode | COMBO | any_profile: tag is in at least one selected profile (union) | all_profiles: tag is in every selected profile (intersection) | |
| profile_2opt | COMBO | (none) | 25 options: (none), pos | ★ Background, pos | ★ Body, pos | ★ Body Pose, pos | ★ Camera Style, pos | ★ Character Type, +19 |
| profile_3opt | COMBO | (none) | 25 options: (none), pos | ★ Background, pos | ★ Body, pos | ★ Body Pose, pos | ★ Camera Style, pos | ★ Character Type, +19 |
| profile_4opt | COMBO | (none) | 25 options: (none), pos | ★ Background, pos | ★ Body, pos | ★ Body Pose, pos | ★ Camera Style, pos | ★ Character Type, +19 |
| add_tagsopt | STRING | [split] 'want' list (+): these tags are forced into output 1 even if they are not in the profile. Also editable with the + button on each tag. | |
| remove_tagsopt | STRING | [split] 'don't want' list (-): these tags are forced out to output 2 even if they are in the profile. 'don't want' wins over 'want'. | |
| replacementopt | STRING | [replace] replacement tags (comma separated). Empty = just remove. | |
| insert_atopt | COMBO | [replace] where_first_match: at the first removed tag's position | start / end: beginning or end of the text | |
| if_no_matchopt | COMBO | [replace] when no profile tag is in the text: append / prepend the replacement, or skip (leave the text unchanged) |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| kept | STRING | — |
| rest | STRING | — |