Expand prompt routing map
This commit is contained in:
@@ -60,6 +60,41 @@ call the same core generation functions.
|
|||||||
| `SxCP SDXL Formatter` | `format_sdxl_prompt` | Converts metadata rows or pair metadata into SDXL/tag style prompts. |
|
| `SxCP SDXL Formatter` | `format_sdxl_prompt` | Converts metadata rows or pair metadata into SDXL/tag style prompts. |
|
||||||
| `SxCP Caption Naturalizer` | `naturalize_caption` | Converts rows into more natural sentence captions. |
|
| `SxCP Caption Naturalizer` | `naturalize_caption` | Converts rows into more natural sentence captions. |
|
||||||
|
|
||||||
|
## Node IO Map
|
||||||
|
|
||||||
|
Use this when wiring or debugging a workflow. If the formatter can receive
|
||||||
|
`metadata_json`, prefer wiring metadata instead of only prompt text. Metadata is
|
||||||
|
what keeps cast, role graph, POV labels, camera config, and soft/hard pair state
|
||||||
|
recoverable.
|
||||||
|
|
||||||
|
| Node | Important inputs | Important outputs |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `SxCP Prompt Builder` | category, subcategory, seed, optional config nodes | `prompt`, `negative_prompt`, `caption`, `metadata_json`, `category`, `subcategory` |
|
||||||
|
| `SxCP Prompt Builder From Configs` | category/cast/profile/filter/config node outputs | Same as `SxCP Prompt Builder` |
|
||||||
|
| `SxCP Insta/OF Prompt Pair` | options, seed_config, character_cast, location/composition/camera, hardcore_position_config | `softcore_prompt`, `hardcore_prompt`, both negatives, both captions, `shared_descriptor`, `metadata_json` |
|
||||||
|
| `SxCP Krea2 Formatter` | `source_text`, optional `metadata_json`, target | `krea_prompt`, both pair prompts if pair metadata exists, negative outputs, method |
|
||||||
|
| `SxCP SDXL Formatter` | `source_text`, optional `metadata_json`, target, style/quality preset | `sdxl_prompt`, both pair prompts if pair metadata exists, negative outputs, method |
|
||||||
|
| `SxCP Caption Naturalizer` | `source_text`, optional `metadata_json` | `natural_caption`, method |
|
||||||
|
|
||||||
|
## Practical Recipes
|
||||||
|
|
||||||
|
These recipes identify the intended road before editing prompt text.
|
||||||
|
|
||||||
|
| Request | Preferred node route | Critical settings | If wrong, inspect |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| Keep character/location but change only sexual pose | `Global Seed` or fixed seed config -> builder/pair | Keep `person_seed` and `scene_seed` fixed; change `pose_seed` and usually `role_seed`; for hardcore categories check `content_seed_axis` | `sexual_poses.json`, `hardcore_position_config`, Krea `_hardcore_action_sentence` |
|
||||||
|
| Generate a specific hardcore oral/blowjob scene | `Hardcore Position Pool` -> `Hardcore Action Filter` -> `Insta/OF Prompt Pair` or `Prompt Builder` | Use `focus=oral_only` or disable non-oral families; keep `allow_oral=true`; constrain position pool to kneeling/standing/oral variants when needed | `sexual_poses.json` oral subcategory/templates, `_apply_hardcore_position_config_to_subcategory`, `_hardcore_action_sentence` |
|
||||||
|
| Generate POV oral or POV penetration | `Man Slot` with POV presence -> `character_cast` -> pair/builder -> Krea2 formatter | POV man must be in the cast; use metadata into Krea2; normal camera directive is suppressed by POV | `_pov_hardcore_pose_sentence`, `_pov_action_phrase`, `_cast_prose` omit-label handling |
|
||||||
|
| Same woman, same room, softcore and hardcore outputs | `Character Slot/Profile` -> `Insta/OF Options` -> `Insta/OF Prompt Pair` | `continuity=same_creator_same_room`; set `softcore_cast` as needed; use pair metadata into formatter | `build_insta_of_pair`, `softcore_row`, `hardcore_row`, pair metadata fields |
|
||||||
|
| Same cast in softcore and hardcore | Character slot chain -> `Insta/OF Options` | `softcore_cast=same_as_hardcore`; configure partner slots/outfits if needed | `_insta_of_partner_styling`, character slot clothing, pair Krea branch |
|
||||||
|
| Change only outfit/clothing | Character clothing or category content route | Keep `person_seed`, `scene_seed`, `pose_seed`; change `content_seed`; slot `softcore_outfit` overrides Insta/OF outfit | `SxCP Character Clothing`, `INSTA_OF_SOFTCORE_OUTFITS`, category item templates |
|
||||||
|
| Force a custom location | `SxCP Location Pool` or `SxCP Location Theme` -> builder/pair | `combine_mode=replace` to force; `add` to mix with category scenes | `_scene_pool`, `_apply_location_config_to_legacy_row`, camera scene adapter |
|
||||||
|
| Force a custom frame/composition | `SxCP Composition Pool` or `SxCP Location Theme` -> builder/pair | `combine_mode=replace` to force; `add` to mix | `_composition_pool`, `_apply_composition_config_to_legacy_row`, Krea composition phrase |
|
||||||
|
| Use Qwen/orbit camera geometry | Qwen/orbit node -> camera_config -> builder/pair | For pair, use `softcore_camera_config` and/or `hardcore_camera_config`; set mode from config in options | `_camera_config_with_mode`, `_camera_directive`, `_camera_scene_directive_for_context` |
|
||||||
|
| Use Krea2 for only hard prompt from a pair | Pair `metadata_json` -> Krea2 Formatter | `target=hardcore`, `input_hint=metadata_json` or auto with metadata connected | `_insta_pair_to_krea`, hard row fields |
|
||||||
|
| Convert builder output to SDXL tags | Builder/pair metadata -> SDXL Formatter | Use metadata input; set `target`; select style and quality preset | `_row_core_tags`, `_soft_tags`, `_hard_tags` |
|
||||||
|
| Save/reuse character | Slot/profile nodes -> Profile Save/Load -> slot/builder | Save from the row/profile data you want, not a freshly randomized disconnected route | profile helpers, `web/profile_buttons.js`, profile JSON |
|
||||||
|
|
||||||
## Seed Axes
|
## Seed Axes
|
||||||
|
|
||||||
Seed routing is centralized around `SEED_AXIS_SALTS`, `SEED_AXIS_ALIASES`, and
|
Seed routing is centralized around `SEED_AXIS_SALTS`, `SEED_AXIS_ALIASES`, and
|
||||||
@@ -82,6 +117,26 @@ Seed routing is centralized around `SEED_AXIS_SALTS`, `SEED_AXIS_ALIASES`, and
|
|||||||
axis. Fixed axis seeds allow changing only one road, for example changing
|
axis. Fixed axis seeds allow changing only one road, for example changing
|
||||||
`pose`/`role` while keeping person, scene, and category stable.
|
`pose`/`role` while keeping person, scene, and category stable.
|
||||||
|
|
||||||
|
## Seed Playbook
|
||||||
|
|
||||||
|
The seed system has two levels: the main row seed and optional per-axis seeds.
|
||||||
|
If an axis seed is negative or absent, the main row seed plus row number drives
|
||||||
|
that axis. If an axis seed is fixed, that axis is reproducible even while other
|
||||||
|
axes change.
|
||||||
|
|
||||||
|
| Goal | Seed setup |
|
||||||
|
| --- | --- |
|
||||||
|
| Exact full regeneration | Keep main `seed`, `row_number`, `start_index`, and every connected config identical. |
|
||||||
|
| Same person, new pose | Fix `person_seed`; change `pose_seed` and usually `role_seed`. For hardcore pose categories, changing `content_seed` may also matter if the selected category uses content for pose items. |
|
||||||
|
| Same scene, new character | Fix `scene_seed`; change `person_seed`. |
|
||||||
|
| Same action, new framing | Fix `pose_seed`, `role_seed`, and `content_seed`; change `composition_seed` and/or camera config. |
|
||||||
|
| Same outfit, new pose | Fix `content_seed`; change `pose_seed`/`role_seed`. |
|
||||||
|
| Same soft/hard pair but different hardcore action | In pair mode, keep `person_seed`, `scene_seed`, `content_seed` if clothing must stay; change `pose_seed`/`role_seed`. |
|
||||||
|
| Debug expression only | Fix everything except `expression_seed` or expression intensity. |
|
||||||
|
|
||||||
|
Common trap: `row_number` participates in `_axis_rng`. If two workflows have the
|
||||||
|
same seeds but different `row_number`, they are not expected to match.
|
||||||
|
|
||||||
## Category Sources
|
## Category Sources
|
||||||
|
|
||||||
There are two category systems.
|
There are two category systems.
|
||||||
@@ -140,6 +195,25 @@ Current category/pool files:
|
|||||||
| `categories/location_pools.json` | Named scene pools and location pool extensions. |
|
| `categories/location_pools.json` | Named scene pools and location pool extensions. |
|
||||||
| `categories/expression_composition_pools.json` | Named expression pools and composition pools. |
|
| `categories/expression_composition_pools.json` | Named expression pools and composition pools. |
|
||||||
|
|
||||||
|
## Pool Ownership Matrix
|
||||||
|
|
||||||
|
This table is the first stop when the selected content is wrong.
|
||||||
|
|
||||||
|
| File / pool area | Owns | Selection axis | Formatter risk |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `default_categories.json` woman casual subcategories | Casual outfit items, casual scenes, casual expressions, casual compositions | `category`, `subcategory`, `content`, `scene`, `expression`, `composition` | Low unless Krea/SDXL needs shorter clothing tags |
|
||||||
|
| `default_categories.json` men casual subcategories | Male casual outfit/items and men-specific casual pools | Same as above | Medium if men are part of a mixed cast and clothing detail is too strong |
|
||||||
|
| `default_categories.json` couple casual subcategories | Couple outfit/action-ish soft poses and couple pools | Same as above | Medium because labels and partner styling can duplicate pair mode |
|
||||||
|
| `erotic_clothes.json` | Provocative/erotic clothing categories and softcore creator scenes | `content`, `scene`, `expression`, `composition` | Medium because nude/implied-nude wording can conflict with clothes |
|
||||||
|
| `sexual_poses.json` foreplay/oral/outercourse/penetration/etc. | Hardcore action item templates, role graphs, axis values, hardcore pool references | `pose` for pose-content route, also `role`; sometimes `content` aliases matter | High because Krea2 rewrites action and POV position text |
|
||||||
|
| `location_pools.json` | Reusable scene pools and legacy scene extensions | `scene` | Medium when a camera-aware adapter changes scene/composition wording |
|
||||||
|
| `expression_composition_pools.json` | Reusable expressions and framing/composition pools | `expression`, `composition` | Medium because formatter may label or suppress expressions |
|
||||||
|
| `generate_prompt_batches.py` legacy pools | Built-in generator clothing, pose, expression, scene, composition lists | Main row seed plus axis config through legacy adapter | Medium because legacy prompt format is field-label heavy |
|
||||||
|
|
||||||
|
When adding a new pool, choose JSON when the change is pure selectable wording.
|
||||||
|
Choose Python only when selection logic, compatibility filters, camera adaptation,
|
||||||
|
profile behavior, or formatter rewriting must change.
|
||||||
|
|
||||||
## Pool Resolution
|
## Pool Resolution
|
||||||
|
|
||||||
### Scene / Location
|
### Scene / Location
|
||||||
@@ -297,6 +371,69 @@ Continuity:
|
|||||||
- `same_creator_new_scene` lets hardcore use its own scene.
|
- `same_creator_new_scene` lets hardcore use its own scene.
|
||||||
- Shared cast descriptors are stored in pair metadata and consumed by formatters.
|
- Shared cast descriptors are stored in pair metadata and consumed by formatters.
|
||||||
|
|
||||||
|
## Metadata Field Dictionary
|
||||||
|
|
||||||
|
The builder outputs JSON metadata because downstream formatters need more than
|
||||||
|
plain prompt text. When debugging, inspect these fields before editing pools.
|
||||||
|
|
||||||
|
### Normal Row Metadata
|
||||||
|
|
||||||
|
| Field | Owner | Consumed by | Meaning |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `source` | `build_prompt` / row builder | All formatters | Usually `json_category` or `built_in_generator`; tells which route created the row. |
|
||||||
|
| `main_category`, `subcategory` | Category selection | All formatters and debug | Human-readable selected category route. |
|
||||||
|
| `category_slug`, `subcategory_slug` | JSON category normalization | Debug/filtering | Stable-ish machine labels for selected category route. |
|
||||||
|
| `content_seed_axis` | `_build_custom_row` | Debug | Shows whether the item/action was driven by `content` or `pose`. Critical for hardcore pose categories. |
|
||||||
|
| `item` | `_compose_item` or Insta override | Krea/SDXL/Naturalizer | Clothing item, category item, or sexual scene/action text. |
|
||||||
|
| `item_axis_values` | `_compose_item` | Krea hardcore rewrite, SDXL tags | Filled template axes such as position/action/detail values. |
|
||||||
|
| `custom_item`, `item_label` | Category/pair route | Formatters and debug | Label/name for item route. |
|
||||||
|
| `role_graph` | `_role_graph`, POV adapter | Krea/Naturalizer | Choreography/action relationship text after POV adaptation. |
|
||||||
|
| `source_role_graph` | `_role_graph` before POV rewrite | Krea hardcore rewrite | Raw action graph used to infer position and contact. |
|
||||||
|
| `scene_text` | `_scene_pool` or location config | All formatters | Final location text. |
|
||||||
|
| `source_scene_text` | location/body-exposure/camera adapters | Debug/continuity | Previous scene text before an override. |
|
||||||
|
| `location_config` | Location config parser | Debug | Active location pool config, if connected. |
|
||||||
|
| `pose` | `_pose_pool` or category item route | Formatters | Generic pose text. Less important for hardcore action categories than `item`/`role_graph`. |
|
||||||
|
| `expression` | `_expression_pool` and intensity filter | All formatters | Final expression text unless disabled. |
|
||||||
|
| `shared_expression` | Expression selection | Debug | Expression before character-specific expansion. |
|
||||||
|
| `character_expression_text` | Character slot expression route | Krea/Naturalizer | Per-character expression clauses. |
|
||||||
|
| `expression_enabled`, `expression_disabled` | Builder/slot override | All formatters | Hard gate for whether expression text should appear. |
|
||||||
|
| `expression_intensity_source` | Builder/slot override | Debug | Explains whether intensity came from input, random, slot, or disabled state. |
|
||||||
|
| `composition` | `_composition_pool`, POV/camera adapter | All formatters | Final framing phrase. |
|
||||||
|
| `source_composition` | Composition adapter | Krea hardcore rewrite | Previous/raw composition, often better for action inference. |
|
||||||
|
| `composition_config` | Composition config parser | Debug | Active composition pool config, if connected. |
|
||||||
|
| `camera_config` | Camera nodes/parser | Krea/SDXL/debug | Structured camera settings. |
|
||||||
|
| `camera_directive` | `_camera_directive` | Krea/Naturalizer/prompt text | Human camera sentence. Suppressed for POV. |
|
||||||
|
| `camera_scene_directive` | scene-camera adapter | Krea/Naturalizer/prompt text | Location-aware camera layout sentence. |
|
||||||
|
| `subject_type`, `subject_phrase` | Subject/context builder | Formatters | Single/couple/group/configured cast route. |
|
||||||
|
| `women_count`, `men_count`, `person_count` | Cast route | Pair/formatters/debug | Effective cast counts. |
|
||||||
|
| `cast_descriptors`, `cast_descriptor_text` | Character/cast route | Krea/SDXL/Naturalizer | Visible cast descriptors. |
|
||||||
|
| `character_cast_slots` | Character slot chain | POV/camera/formatters | Raw configured slots. |
|
||||||
|
| `character_slot_status`, `character_profile_status` | Character/profile application | Debug | Explains whether slot/profile was applied or skipped. |
|
||||||
|
| `pov_character_labels` | Character slot presence mode | Krea/prompt/camera | Labels omitted from visible cast and rewritten as first-person POV. |
|
||||||
|
| `hardcore_position_config` | Hardcore position/filter nodes | Debug | Active hardcore family/position/action constraints. |
|
||||||
|
| `negative_prompt` | Category/pair/default negative route | Formatter output | Base negative text before formatter extras. |
|
||||||
|
| `trigger` | Builder input | Formatter/fallback/debug | Active trigger after fallback to default. |
|
||||||
|
|
||||||
|
### Insta/OF Pair Metadata
|
||||||
|
|
||||||
|
| Field | Owner | Consumed by | Meaning |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `mode` | `build_insta_of_pair` | Formatters | `Insta/OF` selects pair formatter branches. |
|
||||||
|
| `options` | `SxCP Insta/OF Options` | Formatters/debug | Soft/hard level, cast mode, continuity, camera modes, expression settings. |
|
||||||
|
| `shared_descriptor` | Soft row descriptor | Pair formatters | Primary creator descriptor. |
|
||||||
|
| `shared_cast_descriptors` | Cast descriptor builder | Pair formatters | Full cast descriptor list. |
|
||||||
|
| `softcore_row`, `hardcore_row` | Pair route | Pair formatters | Full normal metadata rows for each side. |
|
||||||
|
| `softcore_prompt`, `hardcore_prompt` | Pair assembly | Direct output/fallback | Raw pair prompts before formatter rewrite. |
|
||||||
|
| `softcore_negative_prompt`, `hardcore_negative_prompt` | Pair assembly | Formatter negatives | Separate negatives for each side. |
|
||||||
|
| `softcore_partner_styling` | `_insta_of_partner_styling` | Krea/SDXL pair branch | Partner softcore clothing and pose when same-cast softcore is enabled. |
|
||||||
|
| `character_hardcore_clothing` | Character slots | Krea pair branch | Explicit per-character hardcore clothing state. |
|
||||||
|
| `default_man_hardcore_clothing` | Pair fallback | Krea pair branch | Auto clothing for visible men without configured clothing. |
|
||||||
|
| `hardcore_clothing_state` | Pair clothing continuity | Krea/SDXL pair branch | Final hard clothing/body exposure sentence before Krea cleanup. |
|
||||||
|
| `hardcore_detail_density` | Insta/OF options | Krea hardcore action rewrite | Controls compact/balanced/dense action detail. |
|
||||||
|
| `softcore_camera_config`, `hardcore_camera_config` | Pair camera route | Krea/SDXL pair branch | Separate camera configs after option mode resolution. |
|
||||||
|
| `softcore_camera_directive`, `hardcore_camera_directive` | Pair camera route | Krea pair branch | Separate plain camera sentences, suppressed for POV. |
|
||||||
|
| `softcore_camera_scene_directive`, `hardcore_camera_scene_directive` | Scene-camera adapter | Krea/Naturalizer pair branch | Separate location-aware camera layout text. |
|
||||||
|
|
||||||
## Hardcore Position Route
|
## Hardcore Position Route
|
||||||
|
|
||||||
`SxCP Hardcore Position Pool` and `SxCP Hardcore Action Filter` both emit
|
`SxCP Hardcore Position Pool` and `SxCP Hardcore Action Filter` both emit
|
||||||
@@ -380,6 +517,19 @@ Key Krea2 ownership:
|
|||||||
- Clothing state cleanup: `_natural_clothing_state`.
|
- Clothing state cleanup: `_natural_clothing_state`.
|
||||||
- Camera scene preservation: `_camera_scene_phrase`.
|
- Camera scene preservation: `_camera_scene_phrase`.
|
||||||
|
|
||||||
|
Krea2 field consumption:
|
||||||
|
|
||||||
|
| Branch | Reads most from | Key functions |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Normal single row | `subject_type`, `item`, `pose`, `scene_text`, `expression`, `composition`, `camera_*`, style fields | `_normal_row_to_krea` |
|
||||||
|
| Normal configured cast/hardcore row | `cast_descriptor_text`, `women_count`, `men_count`, `source_role_graph`, `role_graph`, `item`, `item_axis_values`, `source_composition`, `pov_character_labels` | `_normal_row_to_krea`, `_hardcore_action_sentence`, `_pov_action_phrase` |
|
||||||
|
| Insta/OF pair softcore | `shared_descriptor`, `softcore_row`, `softcore_partner_styling`, options, soft camera fields | `_insta_pair_to_krea` |
|
||||||
|
| Insta/OF pair hardcore | `hardcore_row`, `shared_cast_descriptors`, `hardcore_clothing_state`, `hardcore_detail_density`, hard camera fields, POV labels | `_insta_pair_to_krea`, `_hardcore_action_sentence`, `_pov_action_phrase`, `_natural_clothing_state` |
|
||||||
|
| Plain text fallback | `source_text` only | `_fallback_text_to_krea` |
|
||||||
|
|
||||||
|
If metadata is connected and `method` says `text(fallback)`, the formatter did
|
||||||
|
not parse metadata. That is a wiring/input-hint issue, not a prompt pool issue.
|
||||||
|
|
||||||
### SDXL
|
### SDXL
|
||||||
|
|
||||||
`format_sdxl_prompt` chooses between:
|
`format_sdxl_prompt` chooses between:
|
||||||
@@ -391,6 +541,19 @@ Key Krea2 ownership:
|
|||||||
Use this route for style triggers, weighted tag style, nude weighting, and Pony /
|
Use this route for style triggers, weighted tag style, nude weighting, and Pony /
|
||||||
SDXL quality/style presets.
|
SDXL quality/style presets.
|
||||||
|
|
||||||
|
SDXL field consumption:
|
||||||
|
|
||||||
|
| Branch | Reads most from | Key functions |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Normal metadata | cast descriptors, age/body/skin/hair/eyes, item, role graph, scene, camera config/directive | `_row_core_tags`, `_appearance_tags`, `_camera_tags` |
|
||||||
|
| Pair softcore | `softcore_row`, pair partner styling, root soft camera config | `_soft_tags` |
|
||||||
|
| Pair hardcore | `hardcore_row`, `hardcore_clothing_state`, hard camera fields, hard prompt text | `_hard_tags` |
|
||||||
|
| Text fallback | `source_text`, preserve-trigger setting | `_fallback_text_to_sdxl` |
|
||||||
|
|
||||||
|
SDXL is the right place for model trigger handling, tag ordering, weight syntax,
|
||||||
|
quality/style preset changes, and nude-weight defaults. Do not solve those in
|
||||||
|
JSON category pools unless the raw builder text is also wrong.
|
||||||
|
|
||||||
### Naturalizer
|
### Naturalizer
|
||||||
|
|
||||||
`naturalize_caption` chooses metadata-specific renderers such as
|
`naturalize_caption` chooses metadata-specific renderers such as
|
||||||
@@ -399,6 +562,15 @@ SDXL quality/style presets.
|
|||||||
Use this route when the row metadata is correct but the sentence-style caption is
|
Use this route when the row metadata is correct but the sentence-style caption is
|
||||||
too mechanical or unsuitable for training captions.
|
too mechanical or unsuitable for training captions.
|
||||||
|
|
||||||
|
Naturalizer field consumption:
|
||||||
|
|
||||||
|
| Branch | Reads most from | Key functions |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Normal single/couple/group | subject fields, age/body, item, scene, expression, composition, camera scene | `_single_from_row`, `_couple_from_row`, `_group_or_layout_from_row` |
|
||||||
|
| Configured cast/hardcore | `cast_descriptor_text`, `role_graph`, `item`, `scene_text`, expression, composition | `_configured_cast_from_row` |
|
||||||
|
| Insta/OF pair | `softcore_row`, `hardcore_row`, pair options and continuity | `_insta_pair_from_row` |
|
||||||
|
| Text fallback | `caption` or `prompt` text | `_text_to_prose` |
|
||||||
|
|
||||||
## Utility / Workflow Nodes
|
## Utility / Workflow Nodes
|
||||||
|
|
||||||
These do not own prompt pool wording, but they affect execution and review:
|
These do not own prompt pool wording, but they affect execution and review:
|
||||||
@@ -411,6 +583,24 @@ These do not own prompt pool wording, but they affect execution and review:
|
|||||||
| Persistent text preview | `loop_nodes.py`, `web/preview_any_text.js` | Stores any value as text and keeps it after workflow reload. |
|
| Persistent text preview | `loop_nodes.py`, `web/preview_any_text.js` | Stores any value as text and keeps it after workflow reload. |
|
||||||
| SDXL bucket size | `SxCPSDXLBucketSize` in `__init__.py` | Random/fixed SDXL bucket width and height selection. |
|
| SDXL bucket size | `SxCPSDXLBucketSize` in `__init__.py` | Random/fixed SDXL bucket width and height selection. |
|
||||||
|
|
||||||
|
## Drift Audit Helper
|
||||||
|
|
||||||
|
The map should be checked when adding nodes, category files, or named pools.
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python tools/prompt_map_audit.py
|
||||||
|
```
|
||||||
|
|
||||||
|
The script does not import ComfyUI. It parses the repo and prints:
|
||||||
|
|
||||||
|
- registered display node names and known return names;
|
||||||
|
- per-JSON category counts;
|
||||||
|
- named scene/expression/composition pool inventory.
|
||||||
|
|
||||||
|
Use its output to spot doc drift after adding a new node or pool. If a new node
|
||||||
|
or pool appears there but not in this map, update the relevant route table.
|
||||||
|
|
||||||
## Editing Cheatsheet
|
## Editing Cheatsheet
|
||||||
|
|
||||||
| Symptom | First file/function to inspect |
|
| Symptom | First file/function to inspect |
|
||||||
@@ -433,6 +623,80 @@ These do not own prompt pool wording, but they affect execution and review:
|
|||||||
| Saved profile does not match liked character | Profile save/load path and whether the saved input is row metadata or regenerated slot config. |
|
| Saved profile does not match liked character | Profile save/load path and whether the saved input is row metadata or regenerated slot config. |
|
||||||
| Accumulator preview behavior wrong | `loop_nodes.py` accumulator methods and `web/accumulator_preview.js`. |
|
| Accumulator preview behavior wrong | `loop_nodes.py` accumulator methods and `web/accumulator_preview.js`. |
|
||||||
|
|
||||||
|
## Debug Route Traces
|
||||||
|
|
||||||
|
Use these traces to narrow a problem in one pass.
|
||||||
|
|
||||||
|
### Hardcore action keeps selecting the same family
|
||||||
|
|
||||||
|
1. Check metadata `main_category`, `subcategory`, `content_seed_axis`,
|
||||||
|
`hardcore_position_config`, `item`, `role_graph`, and `item_axis_values`.
|
||||||
|
2. If `hardcore_position_config` disabled most families, the repeated action may
|
||||||
|
be the only compatible pool left.
|
||||||
|
3. Inspect `categories/sexual_poses.json` for the selected subcategory,
|
||||||
|
`item_templates`, `axes`, and `weight`.
|
||||||
|
4. If raw `item` differs but Krea output looks identical, inspect
|
||||||
|
`_hardcore_pose_anchor`, `_hardcore_pose_arrangement`,
|
||||||
|
`_hardcore_item_detail`, and `_hardcore_action_sentence`.
|
||||||
|
|
||||||
|
### POV position is spatially wrong
|
||||||
|
|
||||||
|
1. Confirm `pov_character_labels` includes the intended man label.
|
||||||
|
2. Confirm Krea input uses metadata, not plain prompt fallback.
|
||||||
|
3. Inspect `source_role_graph`, `item`, `source_composition`, and
|
||||||
|
`item_axis_values`.
|
||||||
|
4. Edit `_pov_hardcore_pose_sentence` if the first-person body geometry is
|
||||||
|
wrong.
|
||||||
|
5. Edit `sexual_poses.json` if the raw action lacks enough body-position anchor
|
||||||
|
for any formatter to infer a good POV prompt.
|
||||||
|
|
||||||
|
### Camera disappears or becomes too generic
|
||||||
|
|
||||||
|
1. Check row `camera_config`, `camera_directive`, and `camera_scene_directive`.
|
||||||
|
2. If `camera_detail=off` or `camera_mode=disabled`, missing camera text is
|
||||||
|
expected.
|
||||||
|
3. If POV labels exist, plain `camera_directive` is intentionally suppressed.
|
||||||
|
4. If a location-aware sentence is missing, inspect
|
||||||
|
`_camera_scene_directive_for_context` and the scene detector for that
|
||||||
|
location family.
|
||||||
|
5. If raw metadata has camera text but Krea omits it, inspect `_camera_phrase`,
|
||||||
|
`_pair_camera_phrase`, and `_camera_scene_phrase`.
|
||||||
|
|
||||||
|
### Nude/clothing wording conflicts
|
||||||
|
|
||||||
|
1. Check pair root `hardcore_clothing_state`.
|
||||||
|
2. Check hard row `item` and `source_role_graph` for access flags.
|
||||||
|
3. Character slot `hardcore_clothing` overrides pair fallback clothing.
|
||||||
|
4. For Krea wording, inspect `_natural_clothing_state`.
|
||||||
|
5. For generation wording, inspect `_insta_of_hardcore_clothing_state`,
|
||||||
|
`_hardcore_row_access_flags`, and `character_hardcore_clothing_values`.
|
||||||
|
|
||||||
|
### Softcore contains strange no-contact or bed/action leakage
|
||||||
|
|
||||||
|
1. Check whether the prompt came from pair softcore or normal category builder.
|
||||||
|
2. In pair softcore, inspect `softcore_partner_styling`, `softcore_row.item`,
|
||||||
|
`softcore_row.pose`, and options `softcore_cast`.
|
||||||
|
3. If the raw soft prompt contains awkward defensive clauses, fix
|
||||||
|
`build_insta_of_pair` soft prompt assembly.
|
||||||
|
4. If Krea adds the awkwardness, inspect `_insta_pair_to_krea`.
|
||||||
|
|
||||||
|
### Location composition mentions irrelevant props
|
||||||
|
|
||||||
|
1. Check `scene_text` and `composition` separately.
|
||||||
|
2. If scene is good and composition is bad, edit composition pools, not
|
||||||
|
location pools.
|
||||||
|
3. If a scene-camera adapter rewrote composition, inspect
|
||||||
|
`_coworking_composition_prompt` or the future adapter for that scene family.
|
||||||
|
4. If the issue comes from `Location Theme`, edit `THEMATIC_LOCATION_PRESETS`.
|
||||||
|
|
||||||
|
### Trigger missing after formatting
|
||||||
|
|
||||||
|
1. For builder raw prompts, check `trigger` and `prepend_trigger_to_prompt`.
|
||||||
|
2. For Krea fallback, check `preserve_trigger`; metadata route usually rebuilds
|
||||||
|
prose and does not use prompt text as a raw string.
|
||||||
|
3. For SDXL, trigger handling belongs to `format_sdxl_prompt` style assembly:
|
||||||
|
`trigger`, `prepend_trigger_to_prompt`, `preserve_trigger`, and style preset.
|
||||||
|
|
||||||
## Safe Edit Workflow
|
## Safe Edit Workflow
|
||||||
|
|
||||||
Before changing prompt behavior:
|
Before changing prompt behavior:
|
||||||
|
|||||||
@@ -0,0 +1,154 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Print a lightweight audit for the prompt routing map.
|
||||||
|
|
||||||
|
This intentionally avoids importing the ComfyUI node package. It parses Python
|
||||||
|
and JSON files directly, so it can run in a plain shell without ComfyUI loaded.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import ast
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
|
||||||
|
|
||||||
|
def _literal_or_none(node: ast.AST) -> Any:
|
||||||
|
try:
|
||||||
|
return ast.literal_eval(node)
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _assignment_dict(path: Path, name: str) -> dict[str, Any]:
|
||||||
|
tree = ast.parse(path.read_text(encoding="utf-8"))
|
||||||
|
for node in tree.body:
|
||||||
|
if not isinstance(node, ast.Assign):
|
||||||
|
continue
|
||||||
|
if not any(isinstance(target, ast.Name) and target.id == name for target in node.targets):
|
||||||
|
continue
|
||||||
|
value = _literal_or_none(node.value)
|
||||||
|
return value if isinstance(value, dict) else {}
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
def _class_return_names(path: Path) -> dict[str, tuple[str, ...]]:
|
||||||
|
tree = ast.parse(path.read_text(encoding="utf-8"))
|
||||||
|
result: dict[str, tuple[str, ...]] = {}
|
||||||
|
for node in tree.body:
|
||||||
|
if not isinstance(node, ast.ClassDef) or not node.name.startswith("SxCP"):
|
||||||
|
continue
|
||||||
|
for item in node.body:
|
||||||
|
if not isinstance(item, ast.Assign):
|
||||||
|
continue
|
||||||
|
if not any(isinstance(target, ast.Name) and target.id == "RETURN_NAMES" for target in item.targets):
|
||||||
|
continue
|
||||||
|
value = _literal_or_none(item.value)
|
||||||
|
if isinstance(value, tuple) and all(isinstance(part, str) for part in value):
|
||||||
|
result[node.name] = value
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def _category_summary(path: Path) -> dict[str, Any]:
|
||||||
|
data = json.loads(path.read_text(encoding="utf-8"))
|
||||||
|
categories = data.get("categories") or []
|
||||||
|
subcategory_count = 0
|
||||||
|
item_template_count = 0
|
||||||
|
for category in categories:
|
||||||
|
subcategories = category.get("subcategories") or []
|
||||||
|
subcategory_count += len(subcategories)
|
||||||
|
for subcategory in subcategories:
|
||||||
|
item_template_count += len(subcategory.get("item_templates") or [])
|
||||||
|
for item in subcategory.get("items") or []:
|
||||||
|
if isinstance(item, dict):
|
||||||
|
item_template_count += len(item.get("item_templates") or [])
|
||||||
|
return {
|
||||||
|
"categories": len(categories),
|
||||||
|
"subcategories": subcategory_count,
|
||||||
|
"item_templates": item_template_count,
|
||||||
|
"scene_pools": len(data.get("scene_pools") or {}),
|
||||||
|
"expression_pools": len(data.get("expression_pools") or {}),
|
||||||
|
"composition_pools": len(data.get("composition_pools") or {}),
|
||||||
|
"pool_extensions": len(data.get("pool_extensions") or {}),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _pool_names(path: Path, key: str) -> list[str]:
|
||||||
|
data = json.loads(path.read_text(encoding="utf-8"))
|
||||||
|
pools = data.get(key) or {}
|
||||||
|
return sorted(pools) if isinstance(pools, dict) else []
|
||||||
|
|
||||||
|
|
||||||
|
def print_table(headers: tuple[str, ...], rows: list[tuple[Any, ...]]) -> None:
|
||||||
|
widths = [len(header) for header in headers]
|
||||||
|
for row in rows:
|
||||||
|
for index, value in enumerate(row):
|
||||||
|
widths[index] = max(widths[index], len(str(value)))
|
||||||
|
print("| " + " | ".join(header.ljust(widths[index]) for index, header in enumerate(headers)) + " |")
|
||||||
|
print("| " + " | ".join("-" * width for width in widths) + " |")
|
||||||
|
for row in rows:
|
||||||
|
print("| " + " | ".join(str(value).ljust(widths[index]) for index, value in enumerate(row)) + " |")
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
init_path = ROOT / "__init__.py"
|
||||||
|
loop_path = ROOT / "loop_nodes.py"
|
||||||
|
display = _assignment_dict(init_path, "NODE_DISPLAY_NAME_MAPPINGS")
|
||||||
|
loop_display = _assignment_dict(loop_path, "LOOP_NODE_DISPLAY_NAME_MAPPINGS")
|
||||||
|
display.update(loop_display)
|
||||||
|
returns = _class_return_names(init_path)
|
||||||
|
returns.update(_class_return_names(loop_path))
|
||||||
|
|
||||||
|
print("# Node Display Map")
|
||||||
|
node_rows = []
|
||||||
|
for class_name, display_name in sorted(display.items(), key=lambda item: str(item[1])):
|
||||||
|
return_names = ", ".join(returns.get(class_name, ()))
|
||||||
|
node_rows.append((display_name, class_name, return_names or "(dynamic or unnamed)"))
|
||||||
|
print_table(("Display name", "Class", "Return names"), node_rows)
|
||||||
|
|
||||||
|
print("\n# Category JSON Summary")
|
||||||
|
category_rows = []
|
||||||
|
for path in sorted((ROOT / "categories").glob("*.json")):
|
||||||
|
summary = _category_summary(path)
|
||||||
|
category_rows.append(
|
||||||
|
(
|
||||||
|
path.name,
|
||||||
|
summary["categories"],
|
||||||
|
summary["subcategories"],
|
||||||
|
summary["item_templates"],
|
||||||
|
summary["scene_pools"],
|
||||||
|
summary["expression_pools"],
|
||||||
|
summary["composition_pools"],
|
||||||
|
summary["pool_extensions"],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
print_table(
|
||||||
|
(
|
||||||
|
"File",
|
||||||
|
"Categories",
|
||||||
|
"Subcategories",
|
||||||
|
"Item templates",
|
||||||
|
"Scene pools",
|
||||||
|
"Expression pools",
|
||||||
|
"Composition pools",
|
||||||
|
"Extensions",
|
||||||
|
),
|
||||||
|
category_rows,
|
||||||
|
)
|
||||||
|
|
||||||
|
print("\n# Named Pool Inventory")
|
||||||
|
pool_rows = []
|
||||||
|
for path in sorted((ROOT / "categories").glob("*.json")):
|
||||||
|
for key in ("scene_pools", "expression_pools", "composition_pools"):
|
||||||
|
names = _pool_names(path, key)
|
||||||
|
if names:
|
||||||
|
pool_rows.append((path.name, key, len(names), ", ".join(names[:8]) + (" ..." if len(names) > 8 else "")))
|
||||||
|
print_table(("File", "Pool type", "Count", "First names"), pool_rows)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
Reference in New Issue
Block a user