diff --git a/docs/prompt-pool-routing-map.md b/docs/prompt-pool-routing-map.md new file mode 100644 index 0000000..e250ac4 --- /dev/null +++ b/docs/prompt-pool-routing-map.md @@ -0,0 +1,452 @@ +# Prompt Pool Routing Map + +This document maps the roads that can lead to prompt text. It intentionally does +not duplicate every prompt phrase in the JSON pools. The useful debugging target +is usually: which node path selected which pool, which seed axis controlled it, +and whether the final wording was still raw builder text or rewritten by a +formatter. + +## Mental Model + +There are three layers: + +1. Generation layer: selects category, subcategory, item/action, character + descriptors, scene, expression, pose, composition, and camera directives. +2. Pair/adapter layer: optionally builds a softcore/hardcore pair, applies + continuity, POV handling, camera-aware scene text, and clothing state. +3. Formatter layer: rewrites a metadata row into Krea2 prose, SDXL tags, or a + naturalized caption. + +When a result is wrong, first identify which layer owns the bad text: + +- Raw builder prompt already wrong: edit `prompt_builder.py` or the relevant + `categories/*.json` pool/template. +- Raw builder prompt acceptable, Krea2 output wrong: edit `krea_formatter.py`. +- Raw builder prompt acceptable, SDXL tags wrong: edit `sdxl_formatter.py`. +- Natural caption/training caption wrong: edit `caption_naturalizer.py`. +- UI/preview/loop behavior wrong: edit `__init__.py`, `loop_nodes.py`, or + `web/*.js`. + +## High-Level Routes + +```mermaid +flowchart TD + A[SxCP Prompt Builder] --> B[prompt_builder.build_prompt] + C[SxCP Prompt Builder From Configs] --> D[parse config nodes] + D --> B + E[SxCP Insta/OF Prompt Pair] --> F[prompt_builder.build_insta_of_pair] + F --> B + B --> R[metadata row + prompt + negative + caption] + F --> P[pair metadata: soft row + hard row] + R --> K[SxCP Krea2 Formatter] + R --> S[SxCP SDXL Formatter] + R --> N[SxCP Caption Naturalizer] + P --> K + P --> S + P --> N +``` + +The config nodes mostly emit JSON. The final builder nodes parse that JSON and +call the same core generation functions. + +## Main Entry Points + +| ComfyUI node | Python entry | What it owns | +| --- | --- | --- | +| `SxCP Prompt Builder` | `build_prompt` | Direct single prompt generation. Can use built-in categories or JSON categories. | +| `SxCP Prompt Builder From Configs` | `build_prompt_from_configs` -> `build_prompt` | Same generator, but inputs come from category/cast/profile/filter helper nodes. | +| `SxCP Insta/OF Prompt Pair` | `build_insta_of_pair` | Builds a softcore row and hardcore row with shared cast/continuity options. | +| `SxCP Krea2 Formatter` | `format_krea2_prompt` | Converts metadata rows or pair metadata into Krea2-friendly prose. | +| `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. | + +## Seed Axes + +Seed routing is centralized around `SEED_AXIS_SALTS`, `SEED_AXIS_ALIASES`, and +`_axis_rng` in `prompt_builder.py`. + +| Axis | Controls | +| --- | --- | +| `category` | Main category selection when random/auto. | +| `subcategory` | Subcategory selection. | +| `content` | Clothing item, category item, generated outfit, many softcore outfit choices. | +| `person` | Character appearance when not fully manual. | +| `scene` | Location/scene choice. | +| `pose` | Generic pose choice, and for pose-content categories, the hardcore position/action item. | +| `role` | Role graph / action choreography for sexual pose categories. Falls back through pose aliases. | +| `expression` | Expression choice and expression intensity randomization. | +| `composition` | Composition/framing choice. | + +`SxCP Global Seed`, `SxCP Seed Control`, and `SxCP Seed Locker` all feed +`seed_config`. Values below zero mean the row's main seed still drives that +axis. Fixed axis seeds allow changing only one road, for example changing +`pose`/`role` while keeping person, scene, and category stable. + +## Category Sources + +There are two category systems. + +| Source | Files/functions | Notes | +| --- | --- | --- | +| Built-in legacy generator | `generate_prompt_batches.py`, `_build_direct_builtin_row`, `_build_auto_weighted_row` | Handles legacy `woman`, `man`, `couple`, `group_or_layout`, `auto_weighted`, and `auto_full`. | +| JSON category library | `categories/*.json`, `load_category_library`, `_build_custom_row` | Handles expandable categories such as casual clothes, erotic clothes, and hardcore sexual poses. | + +JSON categories are the scalable system. Add new main categories or subcategories +there unless the behavior needs Python logic. + +## JSON Category Road + +```mermaid +flowchart TD + A[category/subcategory input] --> B[_find_subcategory] + B --> C[item from subcategory.items] + C --> D[_compose_item] + D --> E[item_templates + axes] + B --> F[_scene_pool] + B --> G[_expression_pool] + B --> H[_pose_pool] + B --> I[_composition_pool] + E --> J[role graph / item text / axis values] + F --> R[scene_text] + G --> R + H --> R + I --> R + J --> R[metadata row] +``` + +Important JSON keys: + +- `categories`: main category definitions. +- `subcategories`: selectable subcategories inside a category. +- `items`: item/action entries selected by the content or pose axis. +- `item_templates`: templates with axis placeholders. +- `axes`: values used to fill `item_templates`. +- `scene_pool` / `scene_pools` or direct `scenes`: location road. +- `expression_pool` / `expression_pools` or direct `expressions`: expression road. +- `composition_pool` / `composition_pools` or direct `compositions`: framing road. +- `poses`: category-specific pose fallback. +- `prompt_template` / `caption_template`: final prompt assembly for that category. +- `inherit_scenes`, `inherit_expressions`, `inherit_compositions`: stop or allow + inheritance from category/subcategory/item levels. +- `pool_extensions`: patch legacy pools from JSON. + +Current category/pool files: + +| File | Main ownership | +| --- | --- | +| `categories/default_categories.json` | Casual clothes, men/couple casual variants, normal JSON categories. | +| `categories/erotic_clothes.json` | Provocative/erotic clothing categories and their scene/expression/composition pools. | +| `categories/sexual_poses.json` | Hardcore sexual pose/action categories, role graphs, explicit scene/expression/composition pools. | +| `categories/location_pools.json` | Named scene pools and location pool extensions. | +| `categories/expression_composition_pools.json` | Named expression pools and composition pools. | + +## Pool Resolution + +### Scene / Location + +Scene text is selected by `_scene_pool`. + +Resolution order: + +1. `SxCP Location Pool` / `SxCP Location Theme` with `replace` overrides the + category scene pool. +2. Category/subcategory/item direct `scenes` and referenced scene pools are + merged, unless inheritance is disabled. +3. `SxCP Location Pool` with `add` appends its entries. +4. Fallback is legacy `g.SCENES` or `g.GROUP_SCENES`. + +Edit targets: + +- Add reusable named locations: `categories/location_pools.json`. +- Add category-specific locations: the category JSON file. +- Add quick workflow-only locations: `SxCP Location Pool` custom locations. +- Add themed location packs: `THEMATIC_LOCATION_PRESETS` in `prompt_builder.py`. + +### Expression + +Expression text is selected by `_expression_pool`, then filtered by +`_expression_entries_for_intensity`. + +Resolution order: + +1. Category/subcategory/item direct `expressions` and named expression pools. +2. Inheritance can stop at item or subcategory level. +3. Fallback is legacy `g.EXPRESSIONS`. +4. Character slots can override intensity per softcore/hardcore phase. +5. Expression can be disabled globally or per character slot. + +Edit targets: + +- General expression pools: `categories/expression_composition_pools.json`. +- Hardcore-specific expressions: usually `categories/sexual_poses.json` or named + hardcore expression pools. +- Character-level expression settings: slot config and `_cast_expression_intensity_override`. +- Formatter expression wording: `krea_formatter.py` or `caption_naturalizer.py`. + +### Pose / Action + +Generic pose text is selected by `_pose_pool`. Hardcore sexual pose categories +are different: the sexual position/action is an item/template selected by the +content route, with `content_seed_axis` set to `pose` for pose-content +categories. + +Edit targets: + +- Normal pose pools: legacy `g.POSES`, `g.EVOCATIVE_POSES`, or JSON `poses`. +- Hardcore positions/actions: `categories/sexual_poses.json`. +- Position filtering UI: `build_hardcore_position_pool_json`, + `build_hardcore_action_filter_json`, `_apply_hardcore_position_config_to_subcategory`. +- Krea2 action rewrite, POV position rewrite, cleanup: `krea_formatter.py`. + +### Composition + +Composition text is selected by `_composition_pool`. + +Resolution order: + +1. `SxCP Composition Pool` / `SxCP Location Theme` with `replace` overrides the + category composition pool. +2. Category/subcategory/item direct `compositions` and named composition pools + are merged. +3. `SxCP Composition Pool` with `add` appends entries. +4. Fallback is legacy `g.COMPOSITIONS` or `g.GROUP_COMPOSITIONS`. + +Edit targets: + +- General composition pools: `categories/expression_composition_pools.json`. +- Category-specific composition pools: relevant category JSON. +- Workflow overrides: `SxCP Composition Pool`. +- Camera-aware composition replacements: `_coworking_composition_prompt`. + +## Character Route + +```mermaid +flowchart TD + A[Woman/Man/Character Slot chain] --> B[character_cast JSON] + C[Profile Load] --> D[character_profile JSON] + E[Manual Details / Hair / Body / Age / Eyes / Clothing nodes] --> A + B --> F[_parse_character_cast] + F --> G[_character_slot_label_map] + G --> H[_context_from_character_slot] + H --> R[cast descriptors + slot clothing + expression settings] + D --> I[_apply_character_profile_to_context] + I --> R +``` + +Important behavior: + +- Slot chaining labels the closest slot to the final node as `A` for that gender, + then upstream slots as `B`, `C`, and so on. +- `character_cast` is for multi-character configured casts. `character_profile` + is for reusable primary-character details. +- `SxCP Character Manual Details` and characteristic pool nodes can feed slots. +- `SxCP Character Clothing` can feed `softcore_outfit` and `hardcore_clothing`. +- A man slot can be marked as POV. POV men are omitted from visible cast + descriptors, and camera wording is adapted to first-person view. + +Edit targets: + +- Appearance field generation: `_context_from_character_slot`, + `_character_context_for_label`, `_cast_descriptor_entries`. +- Profile save/load: `SxCPCharacterProfileSave`, + `SxCPCharacterProfileLoad`, profile helpers in `prompt_builder.py`, and + `web/profile_buttons.js`. +- Hair/body/ethnicity list behavior: characteristic config builders in + `prompt_builder.py`. + +## Insta/OF Pair Route + +```mermaid +flowchart TD + O[SxCP Insta/OF Options] --> P[build_insta_of_pair] + C[character_cast] --> P + S[seed_config] --> P + L[location_config] --> P + M[composition_config] --> P + H[hardcore_position_config] --> P + P --> A[soft_row via build_prompt] + P --> B[hard_row via build_prompt] + A --> X[pair metadata] + B --> X + X --> K[Krea2/SDXL/Naturalizer] +``` + +Softcore row: + +- Category comes from `INSTA_OF_SOFTCORE_SUBCATEGORY_BY_LEVEL`. +- Outfit comes from character slot `softcore_outfit` if present, otherwise + `INSTA_OF_SOFTCORE_OUTFITS`. +- Soft pose comes from `INSTA_OF_SOFTCORE_POSES`. +- Partner styling comes from `_insta_of_partner_styling` when softcore cast is + `same_as_hardcore`. + +Hardcore row: + +- Category is always `Hardcore sexual poses`. +- Cast count comes from `SxCP Insta/OF Options`. +- Position/action can be constrained by `SxCP Hardcore Position Pool` and + `SxCP Hardcore Action Filter`. +- Clothing comes from character slot hardcore clothing first, then fallback + `hardcore_clothing_continuity`. +- Men receive default hardcore clothing if visible and not configured. +- POV labels alter action, camera, composition, and visible cast descriptors. + +Continuity: + +- `same_creator_same_room` reuses the softcore scene for hardcore. +- `same_creator_new_scene` lets hardcore use its own scene. +- Shared cast descriptors are stored in pair metadata and consumed by formatters. + +## Hardcore Position Route + +`SxCP Hardcore Position Pool` and `SxCP Hardcore Action Filter` both emit +`hardcore_position_config`. + +```mermaid +flowchart TD + A[Hardcore Position Pool] --> C[hardcore_position_config] + B[Hardcore Action Filter] --> C + C --> D[_filter_hardcore_categories_for_position] + C --> E[_apply_hardcore_position_config_to_subcategory] + E --> F[item_templates / axes in sexual_poses.json] + F --> G[role_graph + item + axis_values] + G --> H[Krea2 action sentence and POV rewrite] +``` + +What each part owns: + +- `sexual_poses.json`: available positions, families, action templates, role + graph templates, and action-specific pool references. +- `prompt_builder.py`: filters which templates/axes remain available. +- `krea_formatter.py`: rewrites the selected action into model-readable prose, + including POV variants and cleanup. + +If one action keeps recurring, inspect: + +1. The enabled position family/action flags. +2. `weight` values in `sexual_poses.json`. +3. Whether the selected subcategory has only a small compatible set after + filtering. +4. Whether the formatter collapses several raw actions into the same final + wording. + +## Camera And Scene Route + +Camera config nodes: + +- `SxCP Camera Control`: direct camera settings. +- `SxCP Camera Orbit Control`: orbit-like camera settings. +- `SxCP Qwen Camera Translator`: converts qwen multi-angle info into + `camera_config`. + +Camera handling: + +1. Camera nodes emit `camera_config`. +2. `build_prompt` calls `_apply_camera_config`. +3. `_camera_directive` creates a plain camera sentence unless disabled/off. +4. `_camera_scene_directive_for_context` can add location-aware camera text. +5. POV rows suppress the normal camera directive and use first-person camera + wording instead. + +Current camera-aware scene adapter: + +- Coworking/business-cafe/office scenes are detected by `_is_coworking_scene`. +- Location profile comes from `_coworking_location_profile`. +- Direction, distance, and elevation details come from `_coworking_direction_detail`, + `_coworking_distance_detail`, and `_coworking_elevation_detail`. +- Composition cleanup for coworking outfit-check wording happens in + `_coworking_composition_prompt`. + +Important POV rule: + +- In POV rows, location anchors must stay behind, beside, or at the frame edges. + The foreground belongs to the POV body/hands and visible partner/action. + +## Formatter Routes + +### Krea2 + +`format_krea2_prompt` chooses between three roads: + +- Pair metadata: `_insta_pair_to_krea`. +- Normal metadata row: `_normal_row_to_krea`. +- Plain text fallback: `_fallback_text_to_krea`. + +Key Krea2 ownership: + +- Cast descriptor naturalization: `_cast_prose`, `_natural_label_text`. +- Hardcore action sentence: `_hardcore_action_sentence`. +- POV hardcore sentence: `_pov_hardcore_pose_sentence`, `_pov_action_phrase`. +- Clothing state cleanup: `_natural_clothing_state`. +- Camera scene preservation: `_camera_scene_phrase`. + +### SDXL + +`format_sdxl_prompt` chooses between: + +- Pair metadata: `_soft_tags` and `_hard_tags`. +- Normal metadata row: `_row_core_tags`. +- Plain text fallback: `_fallback_text_to_sdxl`. + +Use this route for style triggers, weighted tag style, nude weighting, and Pony / +SDXL quality/style presets. + +### Naturalizer + +`naturalize_caption` chooses metadata-specific renderers such as +`_configured_cast_from_row`, `_couple_from_row`, and single/group renderers. + +Use this route when the row metadata is correct but the sentence-style caption is +too mechanical or unsuitable for training captions. + +## Utility / Workflow Nodes + +These do not own prompt pool wording, but they affect execution and review: + +| Node family | Files | Purpose | +| --- | --- | --- | +| Loop nodes | `loop_nodes.py`, `web/loop_slots.js` | While/for loop execution and carry values. | +| Index switch | `loop_nodes.py`, `web/index_switch_slots.js` | Multi-input to selected output, and selected input to multi-output routing. | +| Accumulator | `loop_nodes.py`, `web/accumulator_preview.js` | Stores generated values/images during workflow execution and previews/reorders/deletes them. | +| 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. | + +## Editing Cheatsheet + +| Symptom | First file/function to inspect | +| --- | --- | +| Wrong main category/subcategory frequency | Category node config, `load_category_library`, category JSON weights. | +| Wrong outfit/clothing item | Relevant category JSON, `INSTA_OF_SOFTCORE_OUTFITS`, `SxCP Character Clothing`. | +| Nude/clothing state confusing Krea2 | `build_insta_of_pair` clothing state helpers, then `_natural_clothing_state`. | +| Wrong location | `categories/location_pools.json`, category `scene_pool`, `_scene_pool`. | +| Location good but camera/location layout wrong | `_camera_scene_directive_for_context`, coworking adapter functions. | +| Repeated desk/anchor in POV foreground | Coworking direction/distance/elevation helpers. | +| Wrong expression intensity | Character slot expression settings, `_expression_entries_for_intensity`, expression pools. | +| Expression appears when disabled | `_disable_row_expression`, formatter expression extraction. | +| Same hardcore action repeats | Hardcore filter config, `sexual_poses.json` weights, `_apply_hardcore_position_config_to_subcategory`. | +| Raw hardcore prompt position is vague | `sexual_poses.json` item templates and role graph templates. | +| Krea2 hardcore prompt position is vague | `_hardcore_action_sentence` or `_pov_hardcore_pose_sentence`. | +| Man appears described in POV | POV labels, `_cast_prose` omit labels, `_pov_action_phrase`. | +| Camera prompt missing from Krea2 | Row `camera_directive` / `camera_scene_directive`, then Krea `_camera_phrase`. | +| Trigger missing in Krea2 fallback | `format_krea2_prompt` preserve-trigger fallback behavior. | +| SDXL tags too weak/wrong style | `sdxl_formatter.py` presets and `_row_core_tags` / `_soft_tags` / `_hard_tags`. | +| 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`. | + +## Safe Edit Workflow + +Before changing prompt behavior: + +1. Capture one raw builder output and the formatter output. +2. Check metadata JSON for `source`, `main_category`, `subcategory`, + `content_seed_axis`, `scene_text`, `item`, `role_graph`, `source_role_graph`, + `item_axis_values`, `composition`, `camera_scene_directive`, and + `pov_character_labels`. +3. Decide whether the bug is selection, raw wording, camera adaptation, or + formatter rewrite. +4. Edit the smallest owning pool/template/function. +5. Re-run a small simulation with fixed person/scene/category seeds and only the + target axis varied. + +The repo may have unrelated dirty files during interactive prompt work. Always +stage only the intended files for commits.