444 lines
16 KiB
Markdown
444 lines
16 KiB
Markdown
# ComfyUI Prompt Builder
|
|
|
|
Custom ComfyUI node for building prompts from the existing `generate_prompt_batches.py`
|
|
generator without writing image batches.
|
|
|
|
The node is registered as:
|
|
|
|
- `prompt_builder / SxCP Prompt Builder`
|
|
- `prompt_builder / SxCP Seed Control`
|
|
- `prompt_builder / SxCP Camera Control`
|
|
- `prompt_builder / SxCP Caption Naturalizer`
|
|
- `prompt_builder / SxCP Krea2 Formatter`
|
|
- `prompt_builder / SxCP Insta/OF Options`
|
|
- `prompt_builder / SxCP Insta/OF Prompt Pair`
|
|
|
|
It outputs:
|
|
|
|
- `prompt`
|
|
- `negative_prompt`
|
|
- `caption`
|
|
- `metadata_json`
|
|
- `category`
|
|
- `subcategory`
|
|
|
|
`SxCP Seed Control` outputs `seed_config`, which can be connected to the prompt
|
|
builder's optional `seed_config` input.
|
|
|
|
`SxCP Camera Control` outputs `camera_config`, which can be connected to the
|
|
prompt builder or the Insta/OF pair node. It makes camera/framing first-class
|
|
instead of relying on a weak phrase inside the prompt.
|
|
|
|
Camera controls:
|
|
|
|
- `camera_mode`: `standard`, `handheld_selfie`, `mirror_selfie`,
|
|
`phone_tripod`, `creator_pov`, `bed_selfie`, `bathroom_mirror`,
|
|
`phone_flash`, or `action_cam`.
|
|
- `shot_size`: `auto`, `full_body`, `three_quarter`, `waist_up`, `close_up`,
|
|
or `extreme_close_up`.
|
|
- `angle`: `auto`, `eye_level`, `high_angle`, `low_angle`, `overhead`,
|
|
`side_profile`, `rear_view`, or `mirror_reflection`.
|
|
- `lens`: `auto`, `smartphone_wide`, `ultra_wide`, `portrait_lens`,
|
|
`telephoto`, or `macro_detail`.
|
|
- `distance`: `auto`, `arm_length`, `near_body`, `bedside`, or `room_corner`.
|
|
- `orientation`: `auto`, `vertical_story`, `square_feed`, or `horizontal`.
|
|
- `phone_visibility`: `auto`, `phone_visible`, `phone_hidden`,
|
|
`screen_reflection`, or `ring_light_visible`.
|
|
- `priority`: `soft_hint`, `strong`, or `locked`.
|
|
|
|
`SxCP Caption Naturalizer` rewrites tag-like captions or labeled prompts into
|
|
more natural language. Connect the prompt builder's `metadata_json` output to
|
|
`source_text` for the cleanest result. You can also connect `caption` or
|
|
`prompt`; in that case the node falls back to prompt-label parsing or comma-tag
|
|
cleanup.
|
|
|
|
Naturalizer controls:
|
|
|
|
- `input_hint`: `auto`, `metadata_json`, or `caption_or_prompt`.
|
|
- `detail_level`: `concise`, `balanced`, or `dense`.
|
|
- `style_policy`: `drop_style_tail` removes old fixed style tails; `keep_style_terms`
|
|
keeps style descriptions in the rewritten text.
|
|
- `trigger`: defaults to `sxcppnl7`.
|
|
- `include_trigger`: prepends the trigger as its own sentence.
|
|
|
|
It outputs:
|
|
|
|
- `natural_caption`
|
|
- `method`
|
|
|
|
`SxCP Krea2 Formatter` rewrites an existing prompt or `metadata_json` into a
|
|
Krea2-oriented natural-language paragraph. It is a formatter, not a safety or
|
|
content downgrade pass: hardcore items, role graphs, sexual pose wording, and
|
|
camera controls are preserved. Negative prompts stay separate.
|
|
|
|
Important behavior:
|
|
|
|
- Age wording is preserved deliberately. Phrases like `21-year-old adult`,
|
|
`late 20s adult woman`, and `all participants 21+ and visibly adult` are kept
|
|
because they help avoid unwanted young-looking outputs.
|
|
- Trigger words are removed by default because Krea2 prompting generally reads
|
|
better as natural language. Enable `preserve_trigger` if you still need a LoRA
|
|
trigger in the positive prompt.
|
|
- `style_mode`: `preserve` keeps the current generated style text,
|
|
`photographic` converts the style tail toward creator-photo language, and
|
|
`minimal` omits most style text.
|
|
- For Insta/OF paired metadata, the node returns both `krea_softcore_prompt` and
|
|
`krea_hardcore_prompt`, with separate softcore and hardcore negatives.
|
|
|
|
It outputs:
|
|
|
|
- `krea_prompt`
|
|
- `negative_prompt`
|
|
- `krea_softcore_prompt`
|
|
- `krea_hardcore_prompt`
|
|
- `softcore_negative_prompt`
|
|
- `hardcore_negative_prompt`
|
|
- `method`
|
|
|
|
`SxCP Insta/OF Prompt Pair` is a special paired-output mode. It creates one
|
|
shared primary creator descriptor, then returns both a softcore prompt and a
|
|
hardcore prompt from that same descriptor. This is useful when you want the same
|
|
person/look/scene continuity but need two different prompt strengths.
|
|
|
|
It outputs:
|
|
|
|
- `softcore_prompt`
|
|
- `hardcore_prompt`
|
|
- `softcore_negative_prompt`
|
|
- `hardcore_negative_prompt`
|
|
- `softcore_caption`
|
|
- `hardcore_caption`
|
|
- `shared_descriptor`
|
|
- `metadata_json`
|
|
|
|
`SxCP Insta/OF Options` outputs `options_json`, which can be connected to the
|
|
pair node. Defaults are set so the softcore prompt is solo while the hardcore
|
|
prompt can include partners. It also defaults the camera to handheld selfie
|
|
framing. For stronger camera control, connect `SxCP Camera Control` to the pair
|
|
node's optional `camera_config` input.
|
|
|
|
Options:
|
|
|
|
- `softcore_cast`: `solo` or `same_as_hardcore`.
|
|
- `hardcore_cast`: `use_counts`, `couple`, `threesome`, or `group`.
|
|
- `hardcore_women_count` and `hardcore_men_count`: used when `hardcore_cast` is
|
|
`use_counts`. The pair mode always keeps at least one adult woman as the
|
|
primary creator so the shared descriptor remains valid.
|
|
- `softcore_level`: `social_tease`, `lingerie_tease`, `implied_nude`, or
|
|
`explicit_tease`.
|
|
- `hardcore_level`: `explicit` or `hardcore`.
|
|
- `softcore_expression_intensity`: `0.0` is mild/controlled, `0.5` is sensual,
|
|
`1.0` strongly favors more heated softcore faces.
|
|
- `hardcore_expression_intensity`: `0.0` is controlled, `0.5` is balanced
|
|
hardcore, `1.0` strongly favors ahegao-style, drooling, fucked-out, climax,
|
|
and messy orgasm expressions.
|
|
- `platform_style`: `hybrid`, `instagram`, or `onlyfans`.
|
|
- `continuity`: `same_creator_same_room` keeps the scene/composition aligned;
|
|
`same_creator_new_scene` keeps the same creator descriptor but lets the
|
|
hardcore scene use its own setting.
|
|
- `softcore_camera_mode`: base camera mode for the softcore output.
|
|
- `hardcore_camera_mode`: `same_as_softcore` or a separate base camera mode for
|
|
the hardcore output.
|
|
|
|
## Built-In Categories
|
|
|
|
The node keeps the original generator controls:
|
|
|
|
- `category`: `auto_weighted`, `woman`, `man`, `couple`, `group_or_layout`, or a custom JSON category.
|
|
- `clothing`: `full` or `minimal`.
|
|
- `minimal_clothing_ratio`: `-1` disables mixing; `0.0` to `1.0` mixes minimal/full clothing.
|
|
- `ethnicity`: `any`, `asian`, `white_asian`.
|
|
- `poses`: `standard` or `evocative`.
|
|
- `expression_intensity`: `0.0` favors mild, neutral, controlled expressions;
|
|
`0.5` favors balanced category expressions; `1.0` strongly favors the most
|
|
intense expressions available in the selected category. This affects custom
|
|
JSON categories such as `Provocative erotic clothes` and `Hardcore sexual
|
|
poses`.
|
|
- `standard_pose_ratio`: `-1` disables mixing; `0.0` to `1.0` mixes standard/evocative poses.
|
|
- `backside_bias`: `0.0` to `1.0`, applies to evocative single-subject poses.
|
|
- `figure`: `curvy`, `balanced`, `bombshell`.
|
|
- `no_plus_women`: excludes plus-size women.
|
|
- `no_black`: excludes Black/African-coded women from women-focused pools.
|
|
- Optional `camera_config`: connect `SxCP Camera Control` to force selfie,
|
|
phone, lens, angle, distance, crop, and camera-priority behavior. This applies
|
|
to custom categories too, including `Hardcore sexual poses`.
|
|
|
|
`auto_weighted` uses the original batch mix: mostly women, then men, couples, and
|
|
group/layout rows. Direct categories generate only that selected category.
|
|
|
|
## Custom Categories
|
|
|
|
Add or edit JSON files in `categories/*.json`. Each file can define new main
|
|
categories, subcategories, and optional extensions to the built-in pools. Restart
|
|
or reload ComfyUI after changing JSON so dropdown choices are rebuilt.
|
|
|
|
Included JSON categories:
|
|
|
|
- `Casual clothes`
|
|
- `Provocative erotic clothes`
|
|
- `Hardcore sexual poses`
|
|
|
|
Example:
|
|
|
|
```json
|
|
{
|
|
"version": 1,
|
|
"categories": [
|
|
{
|
|
"name": "Casual clothes",
|
|
"slug": "casual_clothes",
|
|
"subject_type": "woman",
|
|
"item_label": "Clothing",
|
|
"style": "tasteful adult fashion-editorial coloured-pencil comic illustration",
|
|
"subcategories": [
|
|
{
|
|
"name": "Streetwear",
|
|
"slug": "streetwear",
|
|
"items": [
|
|
"oversized hoodie with slim jeans and clean sneakers",
|
|
"cropped bomber jacket with cargo pants and chunky trainers"
|
|
],
|
|
"scenes": [
|
|
{
|
|
"slug": "city_crosswalk",
|
|
"prompt": "sunlit city crosswalk with storefront reflections"
|
|
}
|
|
],
|
|
"poses": [
|
|
"standing with one hand in a pocket",
|
|
"walking forward with a casual runway stride"
|
|
]
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
Custom categories do not need a Python generator. If no `prompt_template` is
|
|
provided, the node uses a generic composer that selects subject appearance,
|
|
scene, pose, expression, composition, and a random item from the selected
|
|
subcategory.
|
|
|
|
Reusable banks can be defined with top-level `scene_pools`,
|
|
`expression_pools`, and `composition_pools` in any `categories/*.json` file.
|
|
Categories, subcategories, and items can reference them with `scene_pools`,
|
|
`expression_pools`, and `composition_pools`; referenced pools are merged with
|
|
any local `scenes`, `expressions`, or `compositions`. This keeps expansion
|
|
scalable without duplicating the same bedroom, selfie, mirror, creator,
|
|
expression, camera, or group-sex framing across every subcategory.
|
|
|
|
Set `"inherit_scenes": false`, `"inherit_expressions": false`, or
|
|
`"inherit_compositions": false` on a subcategory or item when it should use only
|
|
its own pools instead of also inheriting parent category values. This is useful
|
|
for narrow subcategories such as group scenes, fetish sets, outdoor-only sets,
|
|
or any category where generic parent wording would be a bad match.
|
|
|
|
Example:
|
|
|
|
```json
|
|
{
|
|
"expression_pools": {
|
|
"creator_tease_faces": [
|
|
"direct creator-shot eye contact",
|
|
"heavy-lidded bedroom gaze"
|
|
]
|
|
},
|
|
"composition_pools": {
|
|
"creator_selfie_frames": [
|
|
"handheld selfie crop from face to hips",
|
|
"mirror selfie with phone visible and body framed clearly"
|
|
]
|
|
},
|
|
"scene_pools": {
|
|
"creator_selfie_rooms": [
|
|
{
|
|
"slug": "bedroom_phone_tripod",
|
|
"prompt": "private creator bedroom with a phone tripod, rumpled bedding, and warm lamps"
|
|
}
|
|
]
|
|
},
|
|
"categories": [
|
|
{
|
|
"name": "Example",
|
|
"subcategories": [
|
|
{
|
|
"name": "Selfie set",
|
|
"inherit_scenes": false,
|
|
"inherit_expressions": false,
|
|
"inherit_compositions": false,
|
|
"scene_pools": ["creator_selfie_rooms"],
|
|
"expression_pools": ["creator_tease_faces"],
|
|
"composition_pools": ["creator_selfie_frames"],
|
|
"items": ["simple outfit prompt"]
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
For large categories, prefer `item_templates` plus `item_axes` instead of writing
|
|
every final item by hand:
|
|
|
|
```json
|
|
{
|
|
"name": "Example clothes",
|
|
"subject_type": "woman",
|
|
"subcategories": [
|
|
{
|
|
"name": "Lingerie",
|
|
"item_templates": [
|
|
"{color} {fabric} {top} with {bottom} and {stockings}"
|
|
],
|
|
"item_axes": {
|
|
"color": ["black", "red", "ivory"],
|
|
"fabric": ["lace", "satin", "transparent mesh"],
|
|
"top": ["balconette bra", "open-cup bra"],
|
|
"bottom": ["matching thong", "high-cut g-string"],
|
|
"stockings": ["thigh-high stockings", "fishnet stockings"]
|
|
}
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
The node chooses one template, fills each placeholder from the matching axis,
|
|
then records the selected axis values in `metadata_json`.
|
|
|
|
Supported `subject_type` values:
|
|
|
|
- `single_any`
|
|
- `woman`
|
|
- `man`
|
|
- `couple`
|
|
- `group`
|
|
- `layout`
|
|
- `scene`
|
|
- `configured_cast`
|
|
|
|
`configured_cast` uses the node's `women_count` and `men_count` inputs. It adds
|
|
template fields for `{women_count}`, `{men_count}`, `{person_count}`,
|
|
`{cast_summary}`, `{scene_kind}`, and `{role_graph}`. This is intended for
|
|
categories where the same prompt generator should support couples, threesomes,
|
|
and larger groups.
|
|
|
|
`{role_graph}` is a generated choreography sentence that assigns roles to the
|
|
cast, such as who penetrates, who receives oral, and who joins from the side.
|
|
It is currently most useful for `Hardcore sexual poses`.
|
|
|
|
Subcategories, templates, and axis values can declare cast constraints:
|
|
|
|
```json
|
|
{
|
|
"name": "Threesomes",
|
|
"min_people": 3,
|
|
"item_axes": {
|
|
"act": [
|
|
{
|
|
"text": "strap-on penetration and cunnilingus",
|
|
"cast": "women_only"
|
|
},
|
|
{
|
|
"text": "male/male oral and anal contact",
|
|
"cast": "men_only"
|
|
},
|
|
{
|
|
"text": "front-and-back penetration",
|
|
"cast": "mixed"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
```
|
|
|
|
Supported constraints:
|
|
|
|
- `min_women`, `max_women`
|
|
- `min_men`, `max_men`
|
|
- `min_people`, `max_people`
|
|
- `cast` or `requires`: `women_only`, `men_only`, `mixed`, `has_women`,
|
|
`has_men`, `solo`, `couple`, `threesome`, `group`
|
|
|
|
If an exact subcategory is not compatible with `women_count` and `men_count`,
|
|
the node raises a clear error instead of generating an impossible prompt.
|
|
|
|
Use the `subcategory` dropdown to select either `random` or an exact
|
|
`Main category / Subcategory` path. Exact paths override the `category` dropdown,
|
|
which is useful because ComfyUI does not provide dependent dropdowns from Python
|
|
alone.
|
|
|
|
## Seed Control
|
|
|
|
The main `seed` input is still the default master seed. Connect `SxCP Seed
|
|
Control` to `seed_config` when you want to lock or vary specific axes.
|
|
|
|
Seed values:
|
|
|
|
- `-1`: follow the main seed.
|
|
- `0` or higher: override only that axis.
|
|
|
|
Axes:
|
|
|
|
- `category_seed`: custom category selection when `custom_random` is used.
|
|
- `subcategory_seed`: random subcategory selection.
|
|
- `content_seed`: generated item content, such as outfit wording.
|
|
- `person_seed`: appearance/person selection.
|
|
- `scene_seed`: scene/environment selection.
|
|
- `pose_seed`: body pose selection. For `Hardcore sexual poses`, this also drives the generated sexual pose content.
|
|
- `role_seed`: participant choreography for `{role_graph}`. If left at `-1`, it follows `pose_seed`.
|
|
- `expression_seed`: facial expression selection.
|
|
- `composition_seed`: camera/composition selection.
|
|
|
|
Example workflow: if the person and scene are right but the pose is wrong, keep
|
|
`person_seed` and `scene_seed` fixed, then change `pose_seed`. If the cast roles
|
|
are wrong but the act wording is good, change `role_seed`. If the clothing or
|
|
sexual act wording is wrong, change `content_seed`; for pose-driven categories,
|
|
change `pose_seed`.
|
|
|
|
## Pool Extensions
|
|
|
|
Use `pool_extensions` to add new entries to built-in pools without editing
|
|
Python:
|
|
|
|
```json
|
|
{
|
|
"pool_extensions": {
|
|
"women_clothes": ["relaxed high-waist jeans with a fitted ribbed tank top"],
|
|
"men_clothes": ["clean white T-shirt with relaxed jeans and canvas sneakers"],
|
|
"poses": ["standing with a relaxed hand-on-hip pose"],
|
|
"expressions": ["easygoing half-smile"],
|
|
"scenes": [
|
|
{
|
|
"slug": "city_cafe",
|
|
"prompt": "quiet city cafe terrace with morning light and small round tables"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
```
|
|
|
|
Known extension pools:
|
|
|
|
`women_clothes`, `women_clothes_minimal`, `men_clothes`, `men_clothes_minimal`,
|
|
`couple_outfits`, `couple_outfits_minimal`, `poses`, `evocative_poses`,
|
|
`backside_poses`, `expressions`, `compositions`, `props`, `figure_curvy`,
|
|
`figure_athletic`, `figure_bombshell`, `scenes`, `group_scenes`,
|
|
`layouts_full`, `layouts_minimal`, `group_compositions`, `group_ages`.
|
|
|
|
## Templates
|
|
|
|
A category, subcategory, or individual item can provide `prompt_template` and
|
|
`caption_template`. Templates can use these fields:
|
|
|
|
`{trigger}`, `{main_category}`, `{subcategory}`, `{item}`, `{item_label}`,
|
|
`{subject}`, `{subject_phrase}`, `{age}`, `{body}`, `{body_phrase}`, `{skin}`,
|
|
`{hair}`, `{eyes}`, `{figure}`, `{scene}`, `{pose}`, `{expression}`,
|
|
`{composition}`, `{style}`, `{positive_suffix}`, `{negative_prompt}`,
|
|
`{women_count}`, `{men_count}`, `{person_count}`, `{cast_summary}`,
|
|
`{scene_kind}`, `{role_graph}`.
|
|
|
|
If `prepend_trigger_to_prompt` is enabled, the node prepends the trigger to the
|
|
positive prompt. Disable it for output closer to the original script's `prompt`
|
|
field.
|