Files
ComfyUI-Ethanfel-Prompt-Bui…/docs/prompt-architecture-improvement-plan.md
T

25 KiB

Prompt Architecture Improvement Plan

This is a working research note for organizing the prompt builder around the routing map in docs/prompt-pool-routing-map.md.

Current Branch Additions

The current branch adds two major surfaces:

  • SxCP Krea2 Resolution Selector in node_seed_resolution.py, with README notes.
  • Expanded hardcore interaction/manual/action pools in categories/sexual_poses.json, categories/expression_composition_pools.json, prompt_builder.py, and krea_formatter.py.

The map audit currently sees:

  • 15 sexual pose subcategories.
  • 94 sexual pose item templates.
  • 23 expression pools.
  • 24 composition pools.
  • A new Krea2 resolution node with width/height/API aspect outputs.

Architectural Finding

The project has a good functional map, but ownership is still mixed inside large files:

  • prompt_builder.py owns selection, character resolution, role graph logic, camera adaptation, pair assembly, and some final string cleanup.
  • krea_formatter.py owns metadata parsing, cast naturalization, sexual action rewriting, POV rewriting, clothing cleanup, camera preservation, fallback parsing, and final prose assembly.
  • sdxl_formatter.py owns tag assembly and style/quality presets.
  • caption_naturalizer.py owns training-caption prose.
  • Category JSON files own scalable pool content, but Python still owns several compatibility and role-graph decisions.

The biggest maintainability risk is not the number of pools. The risk is that selection, semantic rewriting, and final text hygiene are too interleaved. When a prompt has wrong text, it is easy to patch the wrong layer.

First Refactor Boundary

Generic text hygiene now has one home:

  • prompt_hygiene.py

It should only handle route-agnostic cleanup:

  • whitespace and punctuation normalization;
  • empty field-label removal;
  • repeated trigger prefix cleanup;
  • duplicate comma-list item removal;
  • adjacent duplicate sentence cleanup;
  • simple dangling connector cleanup.

It must not make semantic decisions such as sexual action positioning, POV geometry, clothing state, or model-specific tag weighting. Those stay in the route-specific owner. It also preserves ordinary words such as composition inside normal sentences; empty field-label cleanup is limited to standalone labels.

Formatter input/fallback parsing now has one home:

  • formatter_input.py

It owns route-neutral parsing shared by Krea2, SDXL, and natural-caption routes:

  • whitespace and punctuation normalization before formatter parsing;
  • JSON row detection from metadata_json or source text;
  • trigger-prefix stripping with route-specific trigger candidate lists;
  • Avoid: positive/negative splitting for fallback text;
  • the shared prompt field-label inventory and extraction such as Setting:, Sexual scene:, Camera control:, or Composition:;
  • fallback field-label stripping for tag/text routes that need label-free body text;
  • row-value fallback from metadata fields to labeled prompt text.

It must not make formatter-style decisions. Krea prose, SDXL tags, and training caption sentence shape stay in their formatter modules.

Shared hardcore phrase cleanup now has one home:

  • hardcore_text_cleanup.py

It owns environment-anchor normalization used by both prompt generation and Krea formatting, including malformed surface joins and bed/sheet/couch anchors that should become model-neutral body-support language. It must stay route-neutral: no Krea prose, no SDXL tags, and no category selection logic.

Current integration points:

  • prompt_builder.build_prompt
  • prompt_builder.build_insta_of_pair
  • krea_formatter.format_krea2_prompt
  • sdxl_formatter.format_sdxl_prompt
  • caption_naturalizer.naturalize_caption

Target Organization

Generation Layer

Owner: prompt_builder.py plus categories/*.json.

Keep here:

  • category/subcategory/item selection;
  • seed axis routing;
  • character slot/profile resolution;
  • scene/expression/composition pool selection;
  • role graph creation from structured category axes;
  • metadata row construction.

Move or isolate later:

  • pair assembly helpers that still live in prompt_builder.py.

Already isolated:

  • JSON category loading, subcategory normalization, named scene/expression/ composition pool loading, cast compatibility filtering, exact subcategory lookup, and inheritance-based pool merging live in category_library.py.
  • object-style item-template metadata extraction, action/position family normalization, position-key normalization, and metadata audit errors live in category_template_metadata.py.
  • category/cast route preset schemas, config JSON builders, choice lists, and parsers live in category_cast_config.py; prompt_builder.py keeps public delegate wrappers for existing nodes and tests.
  • generation-time cast count phrases, configured-cast context metadata, character-slot label assignment, scene-kind labels, cast-summary wording, and couple count normalization live in cast_context.py; prompt_builder.py keeps delegate wrappers where existing generation paths still call the old helper names.
  • ethnicity/filter choices, advanced filter JSON, ethnicity-list JSON, filter parsing, and ethnicity normalization live in filter_config.py; character routes and builder filters use prompt_builder.py delegate wrappers.
  • character choice lists, descriptor detail/presence/slot-seed normalization, characteristic-list JSON builders/parsers, eye labels, hair config builders/parsers, and hair phrase helpers live in character_config.py; prompt_builder.py still resolves full character slots.
  • character manual-detail config, profile name/path policy, profile JSON normalization, descriptor assembly, save/load/rename/delete operations, fallback profile loading, and context override application live in character_profile.py; prompt_builder.py only bridges generated slot rows into profile saves.
  • generation profile presets, override normalization, trigger policy, and profile config parsing live in generation_profile_config.py; prompt_builder.py keeps public delegate wrappers.
  • location/composition config presets, themed location packs, custom location/composition entry parsing, merge behavior, and config parsing live in location_config.py; built-in row location/composition config application, source metadata, and prompt/caption rewrites live in row_location.py.
  • hardcore position/action-filter choices, selected-position normalization, config JSON builders/parsers, focus-policy toggles, subcategory allow-list policy, position-key detection, category filtering, and item-template/axis filtering live in hardcore_position_config.py.
  • hardcore configured-cast role graph generation lives in hardcore_role_graphs.py; prompt_builder.py selects item/axis metadata and then asks that module for the source role graph.
  • fallback role graph wording lives in hardcore_role_fallback.py, covering solo rows, women-only rows, men-only rows, mixed group fallbacks, and support partner sentences.
  • interaction-style role graph wording lives in hardcore_role_interaction.py, covering foreplay, manual stimulation, body worship, clothing transitions, dominant guidance, camera performance, aftercare, and group coordination.
  • outercourse-specific role graph wording has started moving into action-family modules; hardcore_role_outercourse.py owns boobjob, testicle-sucking, penis-licking, handjob, and footjob body geometry.
  • oral-specific role graph wording lives in hardcore_role_oral.py, including direct POV viewer phrasing for kneeling, face-sitting, sixty-nine, edge-supported, side-lying, chair, standing, and reclining oral positions.
  • penetration-specific role graph wording lives in hardcore_role_penetration.py, covering the main vaginal penetration position families while Krea POV rewriting keeps first-person geometry stable.
  • anal/double-contact role graph wording lives in hardcore_role_anal.py, covering rear-entry anal variants and front/back double-contact source geometry.
  • climax role graph wording lives in hardcore_role_climax.py, covering ejaculation aftermath placement for face/body/ass, lap, open-thigh, side-lying, and front/back group layouts.
  • camera option schema, orbit/Qwen translation, config parsing, camera directive text, and camera caption text live in camera_config.py; camera-scene prose lives in scene_camera_adapters.py; row-level camera insertion, contextual coworking composition mutation, subject-kind detection, and POV suppression live in row_camera.py.
  • shared POV slot detection, label merging/filtering, builder-side POV directives, source role-graph viewer replacement, and shared composition cleanup live in pov_policy.py; prompt builder and Krea POV routes delegate to it.
  • shared hardcore environment-anchor cleanup lives in hardcore_text_cleanup.py and normalizes malformed pool joins before metadata reaches formatter routes.
  • shared hardcore action metadata lives in hardcore_action_metadata.py; custom rows now emit action_family, position_family, position_key, and position_keys so formatter routing and debugging do less keyword guessing. Krea, SDXL, and training-caption routes consume these fields when present.
  • shared row route metadata readers live in route_metadata.py, covering normalized action family, position family/keys, and route-specific formatter hints for Krea, SDXL, and training-caption routes. Position keys are strict by default, while SDXL can opt into legacy unknown key tags for compatibility.
  • final row and pair text normalization lives in row_normalization.py, covering trigger prepending, extra-positive append, negative merge/dedupe, caption-part joining, embedded soft/hard row output synchronization, and row sanitation before metadata leaves generation. It also copies side-specific pair metadata, such as soft partner styling and hardcore clothing/detail state, onto the embedded soft/hard rows.

Pair / Adapter Layer

Owner today: build_insta_of_pair.

Keep here:

  • pair route sequencing;
  • top-level continuity option handoff between row, camera, clothing, and output adapters.

Already isolated:

  • Insta/OF option normalization, softcore category/outfit/pose pools, partner outfit pools, clothing-continuity labels, negatives, and hardcore cast count policy, plus hardcore detail-density directive text, live in pair_options.py; prompt_builder.py keeps public delegate wrappers for existing nodes and tests.
  • soft/hard row creation lives in pair_rows.py, including softcore expression override resolution, Woman A slot context application, soft outfit/pose overrides, POV row fields, and hardcore row creation.
  • pair-level cast/display context lives in pair_cast.py, including descriptor prose, descriptor-entry assembly, shared descriptors, cast-label cleanup, same-cast softcore descriptor text, partner styling, platform and level labels, softcore cast presence text, and hard cast summary text.
  • pair-level camera routing lives in pair_camera.py, including soft/hard camera config selection, same-as-softcore mode, camera-detail override, same-room hard scene continuity, camera-aware composition mutation, POV camera suppression, and row/root camera metadata synchronization.
  • pair-level clothing policy lives in pair_clothing.py, including clothing sentence formatting, body-exposure scene cleanup, action-aware body-access flags, conflicting outfit-piece cleanup, default visible-men clothing, character-clothing override handling, hardcore clothing continuity, and final root clothing-state assembly.
  • final pair output assembly lives in pair_output.py, including soft/hard prompt strings, trigger preservation, negatives, captions, and root metadata shape; the final cleanup step is delegated to row_normalization.py. Embedded soft/hard rows are synchronized to the final pair prompt, caption, and negative outputs during normalization so serialized pair metadata does not carry stale standalone row text. Side-specific structured fields are synchronized there too, including soft partner styling and hardcore clothing continuity metadata.

Krea2 Formatter Path

Owner: krea_formatter.py.

Keep here:

  • Krea prose style;
  • Krea route orchestration;
  • camera-scene preservation;
  • fallback text parsing.

Already isolated:

  • krea_cast.py owns cast descriptor parsing, cast labels, cast prose, label joining, natural cast descriptor text, and label replacement for formatter routes, including the caption naturalizer's cast metadata path.
  • krea_clothing.py owns clothing-state cleanup and action-aware body-access wording for formatter routes.
  • krea_action_context.py owns shared action-family predicates, axis context text, climax detection, and detail-density normalization used by action and POV formatter routes.
  • hardcore_action_metadata.py owns shared action-family constants, normalization, and inference used by the builder and Krea formatter route.
  • pov_policy.py owns shared POV labels, label filtering, source role-graph viewer replacement, and composition cleanup; krea_pov.py owns Krea-specific POV camera support text while delegating shared POV policy.
  • krea_detail.py owns generic detail-clause splitting, deduping, joining, and density limiting for Krea action prose.
  • krea_action_positions.py owns non-POV pose anchors, body-arrangement text, rear-entry detection, and action-position phrasing.
  • krea_action_details.py owns non-climax item/detail cleanup for foreplay, outercourse, oral, penetration, toy/double-contact, and anchor dedupe paths.
  • krea_action_climax.py owns climax-specific role/detail cleanup and aftermath view dedupe.
  • krea_action_dispatch.py owns non-POV role normalization, action-family classification, and family-specific detail cleanup.
  • krea_actions.py owns final non-POV hardcore action sentence assembly.
  • krea_pov_actions.py owns POV hardcore action sentence rewriting, first-person body geometry, and selected-position-axis priority before loose context fallback.
  • formatter_input.py owns shared metadata/source JSON detection, trigger stripping, the shared prompt field-label inventory, prompt-field extraction, Avoid: splitting, and row-value fallback for Krea, SDXL, and caption routes.
  • route_metadata.py owns shared row-level action-family, position-family, position-key, and formatter-hint reads so formatter routes do not normalize these fields independently.

Improve later:

  • keep adding route-level smoke fixtures when new metadata fields start influencing formatter output;

SDXL Formatter Path

Owner: sdxl_formatter.py.

Keep here:

  • trigger behavior;
  • style and quality presets;
  • tag ordering;
  • weighted explicit tags;
  • negative-prompt assembly.
  • metadata-family tag hints from action_family, position_family, and position_keys.
  • shared row route metadata reads from route_metadata.py.
  • shared formatter input parsing from formatter_input.py.
  • style presets, quality presets, default negative prompt, and action/position family tag hints from sdxl_presets.py.
  • formatter profiles for manual controls, Pony flat-vector, SDXL photo, and plain flat-vector styles live in sdxl_presets.py and are exposed by SxCP SDXL Formatter.
  • fallback field-label cleanup delegates to formatter_input.py.

Improve later:

  • add route-level fixtures for any new SDXL model profile that needs different tag ordering.

Naturalizer Path

Owner: caption_naturalizer.py.

Keep here:

  • natural sentence caption assembly;
  • training-caption trigger behavior;
  • style-tail policy from caption_policy.py.
  • metadata-family action labels from action_family and position_family via caption_policy.py.
  • shared row route metadata reads from route_metadata.py.
  • shared formatter input parsing from formatter_input.py.
  • shared cast descriptor parsing and label replacement from krea_cast.py.
  • caption detail-level/style-policy normalization, clothing cleanup, and composition cleanup from caption_policy.py.
  • caption profiles for manual controls, concise training captions, dense training captions, and browsing captions live in caption_policy.py and are exposed by SxCP Caption Naturalizer.

Improve later:

  • add more caption profiles if a new training or browsing workflow needs a distinct default.

Category JSON Path

Owner: categories/*.json.

Keep here:

  • scalable prompt pool content;
  • named scene/expression/composition pools;
  • item templates and axes;
  • direct category-specific wording.
  • optional object-style item templates with route metadata such as action_family, action_type, position_family, family, position_key, position_keys, and formatter_hint; string templates remain valid and fall back to Python inference. Normalized formatter hints are routed into Krea, SDXL, and caption naturalization through all plus the matching formatter route only.

Improve later:

  • keep tools/prompt_map_audit.py passing; it now checks referenced expression/composition/scene pools, item-template axes, and object-template metadata values for both string and object templates.

Node / UI Path

Owner: __init__.py, node_builder.py, node_seed_resolution.py, node_camera.py, node_character.py, node_hardcore_position.py, node_formatter.py, node_insta.py, node_route_config.py, node_profile_filter.py, loop_nodes.py, web/*.js.

Keep here:

  • ComfyUI node input/output declarations;
  • widget behavior;
  • button actions;
  • dynamic input slots.
  • direct and config-driven builder node declarations in node_builder.py.
  • seed and resolution utility node declarations in node_seed_resolution.py.
  • camera utility node declarations in node_camera.py.
  • character pool, slot, and profile node declarations in node_character.py.
  • hardcore position pool/filter node declarations in node_hardcore_position.py.
  • caption/Krea2/SDXL formatter node declarations in node_formatter.py.
  • Insta/OF options and prompt-pair node declarations in node_insta.py.
  • route/category/location/composition/cast config node declarations in node_route_config.py.
  • profile/filter/ethnicity-list node declarations in node_profile_filter.py.

Already isolated:

  • direct and config-driven prompt builder nodes live in node_builder.py, with registration maps imported by __init__.py.
  • seed axis salts/aliases, seed mode choices, lock builders, seed config parsing, row seed math, and deterministic axis RNG live in seed_config.py; seed/global-seed/seed-locker nodes live in node_seed_resolution.py, with registration maps imported by __init__.py.
  • SDXL/Krea2 resolution utility nodes live in node_seed_resolution.py, with registration maps imported by __init__.py.
  • camera/orbit/Qwen translator utility nodes live in node_camera.py, using camera_config.py for option lists and JSON builders, with registration maps imported by __init__.py.
  • hair, age/body/eyes/clothing pools, manual character details, character slots, and profile save/load nodes live in node_character.py, with registration maps imported by __init__.py.
  • hardcore position pool and action filter nodes live in node_hardcore_position.py, with registration maps imported by __init__.py.
  • caption naturalizer, Krea2 formatter, and SDXL formatter nodes live in node_formatter.py, with registration maps imported by __init__.py.
  • Insta/OF options and dual prompt-pair nodes live in node_insta.py, with registration maps imported by __init__.py.
  • category preset, location/composition pool, location theme, and cast config utility nodes live in node_route_config.py, with registration maps imported by __init__.py.
  • generation profile, advanced filter, and ethnicity list utility nodes live in node_profile_filter.py, with registration maps imported by __init__.py.
  • index-switch constants, index-base normalization, missing-input behavior, route-output selection, status text, and lazy-input selection live in index_switch_policy.py; loop_nodes.py keeps the ComfyUI node wrapper and accumulator/loop runtime logic.
  • profile-save and accumulator server payload handling lives in server_routes.py; __init__.py only wires those pure handlers to ComfyUI JSON responses, and tools/prompt_smoke.py covers the handlers without importing ComfyUI.

Improve later:

  • split remaining large node classes into files by family;
  • keep node display names, return names, and docs in sync through the audit helper;
  • add more endpoint tests when new server routes are introduced.

Path-Specific Improvements

Prompt Builder

Near-term:

  • Add final row hygiene already done through prompt_hygiene.py.
  • Add a metadata smoke checker for representative generated rows and static formatter fixtures through tools/prompt_smoke.py.
  • Normalize every row with one function before JSON serialization.

Medium-term:

  • Extract category loading and role graph logic.
  • Convert keyword-heavy interaction filtering to template metadata.

Insta/OF Pair

Near-term:

  • Normalize pair metadata with one helper, including embedded row prompt, caption, negative, and side-specific metadata synchronization.
  • Confirm pair prompts, captions, and soft/hard rows carry the same sanitized scene/camera/clothing fields.
  • Keep same-room pair continuity synchronized in both assembled prompt text and hardcore_row.scene_text; tools/prompt_smoke.py covers this drift case.

Medium-term:

  • Make pair camera and clothing phases explicit subfunctions.
  • Add smoke fixtures for same-cast, POV man, explicit nude, and different-camera modes.

Krea2

Near-term:

  • Add final prose hygiene already done through prompt_hygiene.py.
  • Add smoke coverage through tools/prompt_smoke.py for metadata-driven Krea2 formatting across built-in rows, hardcore rows, same-cast pairs, and POV pairs.
  • Cover camera-scene preservation through tools/prompt_smoke.py for single rows, split soft/hard pair cameras, and POV camera-scene routing.
  • Cover config-node routing through tools/prompt_smoke.py for category, cast, generation profile, seed lock, camera, location theme, and composition config.
  • Cover close foreplay and POV penetration Krea routes so raw labels, invalid surface grammar, normal third-person camera text, and composition punctuation drift are caught.
  • Cover POV outercourse, oral, penetration, anal, and front/back double-contact Krea routes so selected position geometry stays synchronized with metadata.
  • Cover generated climax routes through Krea, SDXL, and natural caption outputs so source aftermath placement and formatter details cannot drift apart.
  • Cover generated interaction routes through Krea, SDXL, and natural caption outputs so source contact/guidance/presentation wording stays metadata-driven.
  • Cover generated fallback role routes through Krea, SDXL, and natural caption outputs so solo and same-sex paths do not remain untested edge behavior.

Medium-term:

  • Dispatch action rewriting by action family.
  • Continue splitting remaining Krea semantic helpers into smaller modules.

SDXL

Near-term:

  • Add final tag hygiene already done through prompt_hygiene.py.
  • Add smoke tests for trigger preservation and duplicate tag removal through tools/prompt_smoke.py.

Medium-term:

  • Make style/quality presets data-driven.

Naturalizer

Near-term:

  • Add final prose hygiene already done through prompt_hygiene.py.
  • Verify training captions keep trigger exactly once through tools/prompt_smoke.py.

Medium-term:

  • Add caption profiles for training and browsing use cases.

Camera / Scene

Near-term:

  • Keep Qwen/orbit as camera source.
  • Keep scene-camera adapters scoped by location family.
  • Use the memory note in /home/ethanfel/.codex/memories/scene-camera-system.md when editing POV.
  • Keep scene_camera_adapters.py as the owner for location-aware camera prose; add new location families there one at a time.
  • Keep row_camera.py as the owner for inserting camera/scene directives into generated rows, including POV suppression of normal third-person camera text.

Medium-term:

  • Build new adapters one location family at a time.

Invariants To Preserve

  • Metadata is the preferred formatter input.
  • Prompt Builder should output structured rows even if raw prompt text is rough.
  • Krea should fix prose and semantic action readability, not category selection.
  • SDXL should produce tag-style output and preserve model triggers as requested.
  • Naturalizer should output training-friendly captions without changing the selected content.
  • Generic cleanup belongs in prompt_hygiene.py; semantic cleanup belongs in the owning route.
  1. Continue splitting remaining __init__.py node classes by family after behavior is covered by smoke checks.
  2. Continue splitting the internals of hardcore_role_graphs.py by action family once generated edge cases are covered by smoke fixtures.
  3. Add more route-level smoke fixtures for generated edge cases that are not covered by the current static Krea/SDXL/caption metadata fixtures.