from __future__ import annotations from dataclasses import dataclass from typing import Any, Callable try: from . import formatter_detail as detail_policy from . import formatter_input as input_policy from . import formatter_route_trace as trace_policy from . import formatter_target as target_policy except ImportError: # pragma: no cover - plain-script smoke tests import formatter_detail as detail_policy import formatter_input as input_policy import formatter_route_trace as trace_policy import formatter_target as target_policy STYLE_MODES = ("preserve", "photographic", "minimal") DEFAULT_STYLE_MODE = "preserve" def style_mode_choices() -> list[str]: return list(STYLE_MODES) def normalize_style_mode(value: Any) -> str: mode = str(value or "").strip().lower().replace("-", "_").replace(" ", "_") return mode if mode in STYLE_MODES else DEFAULT_STYLE_MODE @dataclass(frozen=True) class KreaFormatRequest: 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 = "" @dataclass(frozen=True) class KreaFormatRoute: output: dict[str, str] branch: str method: str target: str detail_level: str style_mode: str @dataclass(frozen=True) class KreaFormatDependencies: trigger_candidates: tuple[str, ...] clean: Callable[[Any], str] row_from_inputs: Callable[[str, str, str], tuple[dict[str, Any] | None, str]] normal_row_to_krea: Callable[[dict[str, Any], str, str], tuple[str, str]] insta_pair_to_krea: Callable[[dict[str, Any], str, str], tuple[str, str, str, str]] fallback_text_to_krea: Callable[[str, bool, str, str], tuple[str, str, str]] append_formatter_hints: Callable[..., str] combine_negative: Callable[..., str] sanitize_prose_text: Callable[..., str] sanitize_negative_text: Callable[[str], str] def format_krea2_prompt_result(request: KreaFormatRequest, deps: KreaFormatDependencies) -> KreaFormatRoute: detail_level = detail_policy.normalize_detail_level(request.detail_level) style_mode = normalize_style_mode(request.style_mode) target = target_policy.normalize_target(request.target) input_hint = input_policy.normalize_input_hint(request.input_hint, text_hint=input_policy.INPUT_HINT_PROMPT) row, method = deps.row_from_inputs(request.source_text, request.metadata_json, request.input_hint) if row and input_policy.is_pair_metadata(row): pair_target = target_policy.pair_policy(target) soft_prompt, soft_negative, hard_prompt, hard_negative = deps.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 = deps.append_formatter_hints(soft_prompt, row, soft_row) hard_prompt = deps.append_formatter_hints(hard_prompt, row, hard_row) if request.extra_positive.strip(): soft_prompt = f"{soft_prompt.rstrip()} {request.extra_positive.strip()}" hard_prompt = f"{hard_prompt.rstrip()} {request.extra_positive.strip()}" soft_prompt = deps.sanitize_prose_text(soft_prompt, triggers=deps.trigger_candidates) hard_prompt = deps.sanitize_prose_text(hard_prompt, triggers=deps.trigger_candidates) selected = hard_prompt if pair_target.selected_side == "hardcore" else soft_prompt selected_negative = hard_negative if pair_target.selected_side == "hardcore" else soft_negative negative = deps.sanitize_negative_text( deps.combine_negative(selected_negative, request.negative_prompt, request.extra_negative) ) output = { "krea_prompt": selected, "negative_prompt": negative, "krea_softcore_prompt": soft_prompt, "krea_hardcore_prompt": hard_prompt, "softcore_negative_prompt": deps.sanitize_negative_text( deps.combine_negative(soft_negative, request.extra_negative) ), "hardcore_negative_prompt": deps.sanitize_negative_text( deps.combine_negative(hard_negative, request.extra_negative) ), "method": f"{method}:krea2(insta_of_pair)", } output["route_trace_json"] = trace_policy.route_trace_json( formatter="krea2", branch="insta_of_pair", method=output["method"], input_hint=input_hint, target=target, detail_level=detail_level, style_mode=style_mode, **trace_policy.metadata_trace_fields(row, target=target, selected_side=pair_target.selected_side), ) return KreaFormatRoute( output=output, branch="insta_of_pair", method=output["method"], target=target, detail_level=detail_level, style_mode=style_mode, ) if row: prompt, kind = deps.normal_row_to_krea(row, detail_level, style_mode) prompt = deps.append_formatter_hints(prompt, row) extracted_negative = deps.clean(row.get("negative_prompt")) method = f"{method}:krea2({kind})" branch = kind else: prompt, extracted_negative, method = deps.fallback_text_to_krea( request.source_text, request.preserve_trigger, detail_level, style_mode, ) branch = "fallback" if request.extra_positive.strip(): prompt = f"{prompt.rstrip()} {request.extra_positive.strip()}" prompt = deps.sanitize_prose_text(prompt, triggers=deps.trigger_candidates) negative = deps.sanitize_negative_text( deps.combine_negative(extracted_negative, request.negative_prompt, request.extra_negative) ) output = { "krea_prompt": prompt, "negative_prompt": negative, "krea_softcore_prompt": "", "krea_hardcore_prompt": "", "softcore_negative_prompt": "", "hardcore_negative_prompt": "", "method": method, } output["route_trace_json"] = trace_policy.route_trace_json( formatter="krea2", branch=branch, method=method, input_hint=input_hint, target=target, detail_level=detail_level, style_mode=style_mode, **trace_policy.metadata_trace_fields(row, target=target), ) return KreaFormatRoute( output=output, branch=branch, method=method, target=target, detail_level=detail_level, style_mode=style_mode, ) def format_krea2_prompt(request: KreaFormatRequest, deps: KreaFormatDependencies) -> dict[str, str]: return format_krea2_prompt_result(request, deps).output