from __future__ import annotations import re from typing import Any try: from . import formatter_input as input_policy from . import route_metadata as route_metadata_policy from .krea_action_context import ( is_close_foreplay_text as _is_close_foreplay_text, is_outercourse_text as _is_outercourse_text, normalize_hardcore_detail_density as _normalize_hardcore_detail_density, ) from . import krea_pair_formatter from .hardcore_text_cleanup import ( sanitize_hardcore_axis_values as _sanitize_hardcore_axis_values, sanitize_hardcore_environment_anchors as _sanitize_hardcore_environment_anchors, ) from .krea_cast import ( cast_prose as _cast_prose, label_join as _label_join, lowercase_for_inline_join as _lowercase_for_inline_join, natural_label_text as _natural_label_text, prompt_cast_descriptors as _prompt_cast_descriptors, ) from .krea_clothing import natural_clothing_state as _natural_clothing_state from .krea_action_positions import action_position_phrase as _action_position_phrase from .krea_actions import hardcore_action_sentence as _hardcore_action_sentence from .krea_pov import ( filter_pov_labeled_clauses as _filter_pov_labeled_clauses, merge_labels as _merge_labels, pov_camera_phrase as _pov_camera_phrase, pov_composition_text as _pov_composition_text, pov_labels_from_value as _pov_labels_from_value, ) from .krea_pov_actions import pov_action_phrase as _pov_action_phrase from .prompt_hygiene import sanitize_negative_text, sanitize_prose_text except ImportError: # Allows local smoke tests with `python -c`. import formatter_input as input_policy import route_metadata as route_metadata_policy from krea_action_context import ( is_close_foreplay_text as _is_close_foreplay_text, is_outercourse_text as _is_outercourse_text, normalize_hardcore_detail_density as _normalize_hardcore_detail_density, ) import krea_pair_formatter from hardcore_text_cleanup import ( sanitize_hardcore_axis_values as _sanitize_hardcore_axis_values, sanitize_hardcore_environment_anchors as _sanitize_hardcore_environment_anchors, ) from krea_cast import ( cast_prose as _cast_prose, label_join as _label_join, lowercase_for_inline_join as _lowercase_for_inline_join, natural_label_text as _natural_label_text, prompt_cast_descriptors as _prompt_cast_descriptors, ) from krea_clothing import natural_clothing_state as _natural_clothing_state from krea_action_positions import action_position_phrase as _action_position_phrase from krea_actions import hardcore_action_sentence as _hardcore_action_sentence from krea_pov import ( filter_pov_labeled_clauses as _filter_pov_labeled_clauses, merge_labels as _merge_labels, pov_camera_phrase as _pov_camera_phrase, pov_composition_text as _pov_composition_text, pov_labels_from_value as _pov_labels_from_value, ) from krea_pov_actions import pov_action_phrase as _pov_action_phrase from prompt_hygiene import sanitize_negative_text, sanitize_prose_text TRIGGER_CANDIDATES = ( "sxcpinup_coloredpencil", "sxcppnl7", ) PROMPT_FIELD_LABELS = input_policy.prompt_field_labels() def _clean(value: Any) -> str: return input_policy.clean_text(value) def _is_false(value: Any) -> bool: if isinstance(value, bool): return value is False if isinstance(value, str): return value.strip().lower() in ("false", "0", "no", "off") return False def _expression_disabled(row: dict[str, Any]) -> bool: return bool(row.get("expression_disabled")) or _is_false(row.get("expression_enabled", True)) def _sentence(text: str) -> str: text = _clean(text).strip(" ,;") if not text: return "" text = text[:1].upper() + text[1:] if text[-1] not in ".!?": text += "." return text def _paragraph(parts: list[str]) -> str: return " ".join(part for part in (_sentence(part) for part in parts) if part) def _formatter_hint_parts(*rows: dict[str, Any]) -> list[str]: hints: list[str] = [] for row in rows: if not isinstance(row, dict): continue for hint in route_metadata_policy.row_formatter_hints(row, "krea"): hint = _clean(hint).strip(" .") if hint and hint not in hints: hints.append(hint) return hints def _append_formatter_hints(prompt: str, *rows: dict[str, Any]) -> str: hints = _formatter_hint_parts(*rows) if not hints: return prompt return _paragraph([prompt, *hints]) def _with_indefinite_article(text: str) -> str: text = _clean(text) if not text or text.lower().startswith(("a ", "an ")): return text article = "an" if text[:1].lower() in "aeiou" else "a" return f"{article} {text}" def _maybe_json(text: str) -> dict[str, Any] | None: return input_policy.maybe_json(text) def _row_from_inputs(source_text: str, metadata_json: str, input_hint: str) -> tuple[dict[str, Any] | None, str]: return input_policy.row_from_inputs(source_text, metadata_json, input_hint) def _strip_trigger(text: str, preserve_trigger: bool) -> str: return input_policy.strip_trigger_prefix(text, TRIGGER_CANDIDATES, preserve_trigger=preserve_trigger) def _split_avoid(text: str) -> tuple[str, str]: return input_policy.split_avoid(text) def _prompt_field(text: str, label: str) -> str: return input_policy.prompt_field(text, label, field_labels=PROMPT_FIELD_LABELS) def _row_value(row: dict[str, Any], key: str, labels: tuple[str, ...] = ()) -> str: return input_policy.row_value(row, key, labels, field_labels=PROMPT_FIELD_LABELS) def _body_phrase(body: Any, figure_note: Any = "") -> str: body = _clean(body) figure_note = _clean(figure_note) if not body: return figure_note if not figure_note: return f"{body} figure" if "figure" in figure_note.lower(): return f"{body} build and {figure_note}" return f"{body} figure with {figure_note}" def _single_caption_front(row: dict[str, Any]) -> dict[str, str]: caption = _strip_trigger(_clean(row.get("caption")), False) if not caption: return {} subject = _clean(row.get("primary_subject")) age = _clean(row.get("age_band") or row.get("age")) body = _clean(row.get("body_phrase")) if not body: body_type = _clean(row.get("body_type") or row.get("body")) figure = _clean(row.get("figure")) body = _body_phrase(body_type, figure) front = f"{subject}, {age}, {body}, " if subject in ("woman", "man") and age and body and caption.startswith(front): try: skin, hair, eyes, _rest = caption[len(front) :].split(", ", 3) except ValueError: return {} return {"body_phrase": body, "skin": skin, "hair": hair, "eyes": eyes} return {} def _combine_negative(*parts: str) -> str: cleaned = [_clean(part).strip(" ,.") for part in parts if _clean(part).strip(" ,.")] return ", ".join(cleaned) def _sanitize_scene_text_for_cast(text: Any, labels: list[str]) -> str: text = _clean(text) if not text: return "" if len(labels) < 3: text = re.sub(r"\s*(?:while|as)\s+another partner watches\b", "", text, flags=re.IGNORECASE) text = re.sub(r"\banother partner watches\b", "", text, flags=re.IGNORECASE) text = re.sub(r",?\s*\bone partner held between two bodies\b", "", text, flags=re.IGNORECASE) text = re.sub(r",?\s*\bthree bodies locked together\b", "", text, flags=re.IGNORECASE) text = re.sub(r",?\s*\bthree bodies\b", "", text, flags=re.IGNORECASE) text = re.sub(r"\bwith\s*,\s*", "with ", text, flags=re.IGNORECASE) text = re.sub(r"\bwhile blowjob\b", "during a blowjob", text, flags=re.IGNORECASE) text = re.sub(r"\bfeaturing blowjob\b", "featuring a blowjob", text, flags=re.IGNORECASE) text = re.sub(r"\s+,", ",", text) text = re.sub(r",\s*,", ",", text) text = re.sub(r"\s{2,}", " ", text).strip(" ,") return text def _composition_phrase( composition: Any, action: str = "", prefix: str = "framed as", detail_density: str = "balanced", ) -> str: composition = _clean(composition) if not composition: return "" action_lower = _clean(action).lower() composition_lower = composition.lower() detail_density = _normalize_hardcore_detail_density(detail_density) if "first-person underview" in action_lower or "straddling the viewer's face" in action_lower: if any(token in composition_lower for token in ("mirror-reflected", "oral scene", "face and body visible")): return ( f"{prefix} close first-person underview with the woman's thighs framing the camera and the oral contact centered" ) if "pov viewer" in action_lower and any( token in action_lower for token in ("ass raised", "seen from behind", "on all fours", "bent forward", "face-down") ): return ( f"{prefix} first-person rear-view frame looking down at the woman's raised ass, with foreground hands and rear-entry contact readable" ) oral_pose_tokens = ( "kneeling oral", "side-lying oral", "spread-leg oral", "standing oral", "edge-of-bed oral", "face-sitting", "sixty-nine", "reclining cunnilingus", "straddled oral", "chair oral", ) if "oral" in action_lower: composition_oral_tokens = [token for token in oral_pose_tokens if token in composition_lower] if composition_oral_tokens and not any(token in action_lower for token in composition_oral_tokens): match = re.search(r"\bwith\s+(.+)$", composition, flags=re.IGNORECASE) return f"framed with {match.group(1)}" if match else "" if _is_outercourse_text(action_lower): return f"{prefix} {composition}" position = _action_position_phrase(action) close_or_aftermath = any( token in composition_lower for token in ("close-up", "close crop", "tight", "direct-flash", "subscriber-view", "post-ejaculation", "aftermath") ) if _is_close_foreplay_text(action_lower) and close_or_aftermath: return f"{prefix} {composition}, in one continuous first-person close-body frame" if position and close_or_aftermath: if detail_density == "compact": return f"{prefix} {composition}, with the {position} still readable" return f"{prefix} {composition}, keeping the {position} and action geography readable" return f"{prefix} {composition}" def _clean_age(age: Any) -> str: return _clean(age) def _age_detail_phrase(age: Any) -> str: text = _clean(age) text = re.sub(r"\s+adults?$", "", text).strip() return text.replace("-year-old", " years old") def _age_subject(row: dict[str, Any], fallback_subject: str = "adult person") -> str: subject = _clean(row.get("subject_phrase") or row.get("primary_subject") or row.get("subject") or fallback_subject) age = _clean_age(row.get("age_band") or row.get("age")) if row.get("subject_type") == "configured_cast": return _clean(row.get("subject_phrase") or subject) if subject in ("woman", "man"): if age: return f"{age} {subject}" if "adult" in age.lower() else f"{age} adult {subject}" return f"adult {subject}" if age and "adult" not in subject.lower(): return f"{age} {subject}" return subject or fallback_subject def _appearance_phrase(row: dict[str, Any]) -> str: front = _single_caption_front(row) parts = [ _row_value(row, "body_phrase") or front.get("body_phrase"), _row_value(row, "skin") or front.get("skin"), _row_value(row, "hair") or front.get("hair"), _row_value(row, "eyes") or front.get("eyes"), ] return ", ".join(_clean(part) for part in parts if _clean(part)) def _expression_phrase(expression: Any) -> str: expression = _clean(expression) if not expression: return "" if ";" in expression or re.search( r"\b(?:Woman|Man) [A-Z] has\b|\bthe (?:woman|man) has\b", expression, flags=re.IGNORECASE, ): return f"Expressions: {expression}" return f"with {expression}" def _camera_phrase(row: dict[str, Any]) -> str: directive = _clean(row.get("camera_directive")) if directive: return directive config = row.get("camera_config") if isinstance(config, dict): detail = _clean(config.get("camera_detail")) if detail == "off" or _clean(config.get("camera_mode")) == "disabled": return "" custom = _clean(config.get("custom_camera_prompt")) if custom: base = _clean(config.get("camera_mode")).replace("_", " ") pieces = [piece for piece in (base, custom) if piece and piece != "standard"] return "Camera: " + ", ".join(pieces) mode = _clean(config.get("camera_mode")).replace("_", " ") shot = _clean(config.get("shot_size")).replace("_", " ") angle = _clean(config.get("angle")).replace("_", " ") pieces = [piece for piece in (mode, shot, angle) if piece and piece != "auto" and piece != "standard"] if pieces: return "Camera: " + ", ".join(pieces) return "" def _camera_scene_phrase(row: dict[str, Any]) -> str: return _clean(row.get("camera_scene_directive")) def _camera_phrase_from_config(config: Any) -> str: if not isinstance(config, dict): return "" detail = _clean(config.get("camera_detail")) if detail == "off" or _clean(config.get("camera_mode")) == "disabled": return "" custom = _clean(config.get("custom_camera_prompt")) if custom: base = _clean(config.get("camera_mode")).replace("_", " ") pieces = [piece for piece in (base, custom) if piece and piece != "standard"] return "Camera: " + ", ".join(pieces) values = [ _clean(config.get("camera_mode")).replace("_", " "), _clean(config.get("shot_size")).replace("_", " "), _clean(config.get("angle")).replace("_", " "), _clean(config.get("lens")).replace("_", " "), _clean(config.get("distance")).replace("_", " "), _clean(config.get("orientation")).replace("_", " "), _clean(config.get("phone_visibility")).replace("_", " "), ] pieces = [value for value in values if value and value not in ("auto", "standard")] if not pieces: return "" return "Camera: " + ", ".join(pieces) def _pair_camera_phrase(directive: Any, config: Any, row: dict[str, Any]) -> str: directive_text = _clean(directive) if directive_text: return directive_text if isinstance(config, dict) and ( _clean(config.get("camera_detail")) == "off" or _clean(config.get("camera_mode")) == "disabled" ): return "" return _camera_phrase_from_config(config) or _camera_phrase(row) def _style_phrase(row: dict[str, Any], style_mode: str) -> str: if style_mode == "minimal": return "" if style_mode == "photographic": return "realistic creator-shot photography with natural lighting, tactile skin and fabric detail, and clean social-media composition" style = _clean(row.get("style")) suffix = _clean(row.get("positive_suffix")) or _prompt_field(_clean(row.get("prompt")), "Use") if style and suffix: return f"{style}; {suffix}" return style or suffix def _couple_clothing_phrase(item: str) -> str: item = _clean(item) lower = item.lower() partner_text = re.sub(r"\bPartner ([AB]) wears\b", r"Partner \1 wearing", item) partner_text = re.sub(r"\bPartner ([AB]) has\b", r"Partner \1 with", partner_text) if lower.startswith("partner a "): return f"The outfits show {partner_text}" if lower.startswith(("two ", "paired ", "coordinated ")): return f"The outfits are {partner_text}" return f"The couple wears {item}" def _normal_row_to_krea(row: dict[str, Any], detail_level: str, style_mode: str) -> tuple[str, str]: subject_type = _clean(row.get("subject_type")) primary = _clean(row.get("primary_subject")) item = _row_value(row, "item", ("Sexual pose", "Erotic outfit", "Clothing")) or _clean(row.get("custom_item")) item = re.sub(r",?\s*(fashion editorial|resort) styling$", "", item, flags=re.IGNORECASE) scene = _row_value(row, "scene_text", ("Setting", "Scene")) or _clean(row.get("scene")) pose = _row_value(row, "pose", ("Sexual pose", "Pose")) expression = "" if not _expression_disabled(row): expression = _row_value(row, "character_expression_text") or _row_value(row, "expression", ("Facial expressions", "Facial expression")) composition = re.sub(r"^vertical\s+", "", _row_value(row, "composition", ("Composition",)), flags=re.IGNORECASE) source_composition = re.sub( r"^vertical\s+", "", _clean(row.get("source_composition")) or composition, flags=re.IGNORECASE, ) camera = _camera_phrase(row) camera_scene = _camera_scene_phrase(row) style = _style_phrase(row, style_mode) if subject_type == "configured_cast" or _clean(row.get("cast_summary")): subject = _clean(row.get("subject_phrase") or primary or "adult sexual scene") cast = _clean(row.get("cast_summary")) try: women_count = int(row.get("women_count") or 0) men_count = int(row.get("men_count") or 0) except (TypeError, ValueError): women_count = men_count = 0 cast_descriptor_text = ( _clean(row.get("cast_descriptor_text")) or _prompt_field(_clean(row.get("prompt")), "Characters") or _prompt_field(_clean(row.get("prompt")), "Cast descriptors") ) pov_labels = _pov_labels_from_value(row.get("pov_character_labels")) if pov_labels: camera = "" cast_prose, cast_labels = _cast_prose(cast_descriptor_text, omit_labels=pov_labels) if not cast_labels and women_count == 1 and men_count == 1: cast_labels = ["Woman A", "Man A"] cast_labels = _merge_labels(cast_labels, pov_labels) expression = _filter_pov_labeled_clauses(expression, pov_labels) expression = _natural_label_text(expression, cast_labels) composition = _sanitize_hardcore_environment_anchors(composition) source_composition = _sanitize_hardcore_environment_anchors(source_composition) role_graph = _sanitize_scene_text_for_cast( _sanitize_hardcore_environment_anchors(row.get("source_role_graph") or row.get("role_graph")), cast_labels, ) item = _sanitize_scene_text_for_cast(_sanitize_hardcore_environment_anchors(item), cast_labels) role_graph = _natural_label_text(role_graph, cast_labels) item = _natural_label_text(item, cast_labels) axis_values = _sanitize_hardcore_axis_values(row.get("item_axis_values")) detail_density = _normalize_hardcore_detail_density(row.get("hardcore_detail_density")) action = _hardcore_action_sentence( role_graph, item, source_composition, axis_values, detail_density, route_metadata_policy.row_action_family(row), ) action = _pov_action_phrase(action, pov_labels, role_graph, item, source_composition, axis_values, detail_density) output_composition = _pov_composition_text(composition, pov_labels) parts = [ action, _pov_camera_phrase(pov_labels), cast_prose, f"A consensual explicit adult scene with {subject}" if not action else "", f"The cast includes {cast}" if cast and not cast_prose and not (women_count == 1 and men_count == 1) else "", f"The setting is {scene}" if scene else "", camera_scene, _expression_phrase(expression), _composition_phrase(output_composition, action, "The image is framed as", detail_density), camera, style if detail_level != "concise" else "", ] return _paragraph(parts), "metadata(configured_cast)" if primary in ("woman", "man") or subject_type in ("woman", "man", "single_any"): subject = _age_subject(row, "adult woman") appearance = _appearance_phrase(row) parts = [ _with_indefinite_article(subject), f"with {appearance}" if appearance else "", f"wearing {item}" if item else "", f"{pose}" if pose else "", f"with {expression}" if expression else "", f"in {scene}" if scene else "", camera_scene, f"framed as {composition}" if composition else "", camera, style if detail_level != "concise" else "", ] return _paragraph([", ".join(part for part in parts[:6] if part), *parts[6:]]), "metadata(single)" if subject_type == "couple" or primary in ("two women", "two men", "a woman and a man"): subject = _clean(row.get("subject_phrase") or primary or "adult couple") if subject == "woman and man": subject = "a woman and a man" ages = _age_detail_phrase(_row_value(row, "age", ("Ages",)) or row.get("age_band")) body = _row_value(row, "body", ("Body types",)) or _clean(row.get("body_type")) parts = [ f"An adult couple: {subject}, all visibly adult", f"Age detail: {ages}" if ages else "", f"Body types: {body}" if body else "", _couple_clothing_phrase(item) if item else "", f"The pose is {pose}" if pose else "", f"The setting is {scene}" if scene else "", camera_scene, f"Facial expressions are {expression}" if expression else "", f"The image is framed as {composition}" if composition else "", camera, style if detail_level != "concise" else "", ] return _paragraph(parts), "metadata(couple)" subject = _age_subject(row, primary or "adult scene") parts = [ f"{subject}", f"featuring {item}" if item else "", f"in {scene}" if scene else "", camera_scene, f"with {expression}" if expression else "", f"framed as {composition}" if composition else "", camera, style if detail_level != "concise" else "", ] return _paragraph(parts), "metadata(generic)" def _krea_pair_format_dependencies() -> krea_pair_formatter.KreaPairFormatDependencies: return krea_pair_formatter.KreaPairFormatDependencies( clean=_clean, prompt_cast_descriptors=_prompt_cast_descriptors, pair_camera_phrase=_pair_camera_phrase, camera_scene_phrase=_camera_scene_phrase, style_phrase=_style_phrase, sanitize_hardcore_environment_anchors=_sanitize_hardcore_environment_anchors, sanitize_hardcore_axis_values=_sanitize_hardcore_axis_values, sanitize_scene_text_for_cast=_sanitize_scene_text_for_cast, normalize_hardcore_detail_density=_normalize_hardcore_detail_density, row_action_family=route_metadata_policy.row_action_family, hardcore_action_sentence=_hardcore_action_sentence, pov_action_phrase=_pov_action_phrase, pov_labels_from_value=_pov_labels_from_value, merge_labels=_merge_labels, cast_prose_omit=lambda text, omit_labels: _cast_prose(text, omit_labels=omit_labels), label_join=_label_join, filter_pov_labeled_clauses=_filter_pov_labeled_clauses, natural_label_text=_natural_label_text, expression_disabled=_expression_disabled, expression_phrase=_expression_phrase, pov_camera_phrase=lambda labels: _pov_camera_phrase(labels), pov_soft_camera_phrase=lambda labels: _pov_camera_phrase(labels, softcore=True), pov_composition_text=_pov_composition_text, natural_clothing_state=_natural_clothing_state, composition_phrase=_composition_phrase, paragraph=_paragraph, combine_negative=_combine_negative, ) def _insta_pair_to_krea(row: dict[str, Any], detail_level: str, style_mode: str) -> tuple[str, str, str, str]: return krea_pair_formatter.format_insta_pair( krea_pair_formatter.KreaPairFormatRequest( row=row, detail_level=detail_level, style_mode=style_mode, ), _krea_pair_format_dependencies(), ) def _fallback_text_to_krea( source_text: str, preserve_trigger: bool, detail_level: str, style_mode: str, ) -> tuple[str, str, str]: positive, negative = _split_avoid(_strip_trigger(source_text, preserve_trigger)) positive = re.sub(r"\b(?:Scene|Setting):", "The setting is", positive) positive = re.sub(r"\b(?:Pose|Sexual pose):", "The pose is", positive) positive = re.sub(r"\bFacial expressions?:", "The facial expression is", positive) positive = re.sub(r"\bComposition:", "The composition is", positive) positive = re.sub(r"\bRole graph:", "The role choreography is", positive) positive = re.sub(r"\bUse\b", "Use", positive) positive = _clean(positive) return _paragraph([positive]), negative, "text(fallback)" def format_krea2_prompt( source_text: str, metadata_json: str = "", negative_prompt: str = "", input_hint: str = "auto", target: str = "auto", detail_level: str = "balanced", style_mode: str = "preserve", preserve_trigger: bool = False, extra_positive: str = "", extra_negative: str = "", ) -> dict[str, str]: detail_level = detail_level if detail_level in ("concise", "balanced", "dense") else "balanced" style_mode = style_mode if style_mode in ("preserve", "photographic", "minimal") else "preserve" target = target if target in ("auto", "single", "softcore", "hardcore") else "auto" row, method = _row_from_inputs(source_text, metadata_json, input_hint) extracted_negative = "" if row and row.get("mode") == "Insta/OF": soft_prompt, soft_negative, hard_prompt, hard_negative = _insta_pair_to_krea(row, detail_level, style_mode) soft_row = row.get("softcore_row") if isinstance(row.get("softcore_row"), dict) else {} hard_row = row.get("hardcore_row") if isinstance(row.get("hardcore_row"), dict) else {} soft_prompt = _append_formatter_hints(soft_prompt, row, soft_row) hard_prompt = _append_formatter_hints(hard_prompt, row, hard_row) if extra_positive.strip(): soft_prompt = f"{soft_prompt.rstrip()} {extra_positive.strip()}" hard_prompt = f"{hard_prompt.rstrip()} {extra_positive.strip()}" soft_prompt = sanitize_prose_text(soft_prompt, triggers=TRIGGER_CANDIDATES) hard_prompt = sanitize_prose_text(hard_prompt, triggers=TRIGGER_CANDIDATES) selected = hard_prompt if target == "hardcore" else soft_prompt if target == "softcore" else soft_prompt selected_negative = hard_negative if target == "hardcore" else soft_negative negative = sanitize_negative_text(_combine_negative(selected_negative, negative_prompt, extra_negative)) return { "krea_prompt": selected, "negative_prompt": negative, "krea_softcore_prompt": soft_prompt, "krea_hardcore_prompt": hard_prompt, "softcore_negative_prompt": sanitize_negative_text(_combine_negative(soft_negative, extra_negative)), "hardcore_negative_prompt": sanitize_negative_text(_combine_negative(hard_negative, extra_negative)), "method": f"{method}:krea2(insta_of_pair)", } if row: prompt, kind = _normal_row_to_krea(row, detail_level, style_mode) prompt = _append_formatter_hints(prompt, row) extracted_negative = _clean(row.get("negative_prompt")) method = f"{method}:krea2({kind})" else: prompt, extracted_negative, method = _fallback_text_to_krea(source_text, preserve_trigger, detail_level, style_mode) if extra_positive.strip(): prompt = f"{prompt.rstrip()} {extra_positive.strip()}" prompt = sanitize_prose_text(prompt, triggers=TRIGGER_CANDIDATES) negative = sanitize_negative_text(_combine_negative(extracted_negative, negative_prompt, extra_negative)) return { "krea_prompt": prompt, "negative_prompt": negative, "krea_softcore_prompt": "", "krea_hardcore_prompt": "", "softcore_negative_prompt": "", "hardcore_negative_prompt": "", "method": method, }