diff --git a/docs/prompt-architecture-improvement-plan.md b/docs/prompt-architecture-improvement-plan.md index d546609..7497091 100644 --- a/docs/prompt-architecture-improvement-plan.md +++ b/docs/prompt-architecture-improvement-plan.md @@ -329,12 +329,16 @@ Owner: `krea_formatter.py`. Keep here: - Krea prose style; -- Krea route orchestration; +- Krea top-level route orchestration; - camera-scene preservation; - fallback text parsing. Already isolated: +- `krea_pair_formatter.py` owns Insta/OF pair soft/hard Krea prose assembly + behind `KreaPairFormatRequest`, `KreaPairFormatDependencies`, and + `KreaPairPrompts`; `krea_formatter.py` keeps the `_insta_pair_to_krea` + compatibility wrapper. - `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. diff --git a/docs/prompt-pool-routing-map.md b/docs/prompt-pool-routing-map.md index a6251c4..8badcb8 100644 --- a/docs/prompt-pool-routing-map.md +++ b/docs/prompt-pool-routing-map.md @@ -648,7 +648,8 @@ Important POV rule: `format_krea2_prompt` chooses between three roads: -- Pair metadata: `_insta_pair_to_krea`. +- Pair metadata: `krea_pair_formatter.format_insta_pair_result` through the + `_insta_pair_to_krea` compatibility wrapper. - Normal metadata row: `_normal_row_to_krea`. - Plain text fallback: `_fallback_text_to_krea`. @@ -663,6 +664,8 @@ Key Krea2 ownership: - Climax role/detail cleanup: `krea_action_climax.py`. - Non-POV action-family routing: `krea_action_dispatch.py`. - Non-POV hardcore action sentence: `krea_actions.hardcore_action_sentence`. +- Insta/OF pair soft/hard Krea prose assembly: + `krea_pair_formatter.format_insta_pair_result`. - Shared POV labels/filtering/composition cleanup: `pov_policy.py`. - Krea POV camera support: `krea_pov.py`. - Detail clause splitting and density limiting: `krea_detail.py`. @@ -676,8 +679,8 @@ Krea2 field consumption: | --- | --- | --- | | 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`, `krea_actions.hardcore_action_sentence`, `krea_pov_actions.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`, `krea_actions.hardcore_action_sentence`, `krea_pov_actions.pov_action_phrase`, `krea_clothing.natural_clothing_state` | +| Insta/OF pair softcore | `shared_descriptor`, `softcore_row`, `softcore_partner_styling`, options, soft camera fields | `krea_pair_formatter.format_insta_pair_result` | +| Insta/OF pair hardcore | `hardcore_row`, `shared_cast_descriptors`, `hardcore_clothing_state`, `hardcore_detail_density`, hard camera fields, POV labels | `krea_pair_formatter.format_insta_pair_result`, `krea_actions.hardcore_action_sentence`, `krea_pov_actions.pov_action_phrase`, `krea_clothing.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 diff --git a/krea_formatter.py b/krea_formatter.py index 0c57293..fa99900 100644 --- a/krea_formatter.py +++ b/krea_formatter.py @@ -11,6 +11,7 @@ try: 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, @@ -42,6 +43,7 @@ except ImportError: # Allows local smoke tests with `python -c`. 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, @@ -536,166 +538,46 @@ def _normal_row_to_krea(row: dict[str, Any], detail_level: str, style_mode: str) 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]: - descriptor = _clean(row.get("shared_descriptor")) - cast_descriptors = row.get("shared_cast_descriptors") - if isinstance(cast_descriptors, list): - cast_descriptor_text = "; ".join(_clean(item) for item in cast_descriptors if _clean(item)) - else: - cast_descriptor_text = _clean(cast_descriptors) - cast_descriptor_text = _prompt_cast_descriptors(cast_descriptor_text) - soft = row.get("softcore_row") if isinstance(row.get("softcore_row"), dict) else {} - hard = row.get("hardcore_row") if isinstance(row.get("hardcore_row"), dict) else {} - soft_camera = _pair_camera_phrase(row.get("softcore_camera_directive"), row.get("softcore_camera_config"), soft) - hard_camera = _pair_camera_phrase(row.get("hardcore_camera_directive"), row.get("hardcore_camera_config"), hard) - soft_camera_scene = _camera_scene_phrase(soft) or _clean(row.get("softcore_camera_scene_directive")) - hard_camera_scene = _camera_scene_phrase(hard) or _clean(row.get("hardcore_camera_scene_directive")) - soft_style = _style_phrase(soft, style_mode) - hard_style = _style_phrase(hard, style_mode) - options = row.get("options") if isinstance(row.get("options"), dict) else {} - soft_level = _clean(options.get("softcore_level")).replace("_", " ") - hard_level = _clean(options.get("hardcore_level")).replace("_", " ") - same_room = options.get("continuity") == "same_creator_same_room" - hard_scene = soft.get("scene_text") if same_room and soft.get("scene_text") else hard.get("scene_text") - hard_composition = _sanitize_hardcore_environment_anchors(hard.get("composition")) - hard_source_composition = _sanitize_hardcore_environment_anchors(hard.get("source_composition") or hard_composition) - pov_labels = _merge_labels( - _pov_labels_from_value(row.get("pov_character_labels")), - _pov_labels_from_value(soft.get("pov_character_labels")), - _pov_labels_from_value(hard.get("pov_character_labels")), - ) - if pov_labels: - hard_camera = "" - if options.get("softcore_cast") == "same_as_hardcore": - soft_camera = "" - soft_cast_descriptor_text = ( - cast_descriptor_text - if options.get("softcore_cast") == "same_as_hardcore" - else f"Woman A: {descriptor}" - ) - soft_cast_prose, soft_labels = _cast_prose( - soft_cast_descriptor_text, - omit_labels=pov_labels if options.get("softcore_cast") == "same_as_hardcore" else [], - ) - hard_cast_prose, hard_labels = _cast_prose(cast_descriptor_text, omit_labels=pov_labels) - soft_labels = _merge_labels(soft_labels, pov_labels if options.get("softcore_cast") == "same_as_hardcore" else []) - hard_labels = _merge_labels(hard_labels, pov_labels) - hard_item = _sanitize_scene_text_for_cast(_sanitize_hardcore_environment_anchors(hard.get("item")), hard_labels) - hard_role_graph = _sanitize_scene_text_for_cast( - _sanitize_hardcore_environment_anchors(hard.get("source_role_graph") or hard.get("role_graph")), - hard_labels, - ) - hard_item = _natural_label_text(hard_item, hard_labels) - hard_role_graph = _natural_label_text(hard_role_graph, hard_labels) - hard_axis_values = _sanitize_hardcore_axis_values(hard.get("item_axis_values")) - hard_detail_density = _normalize_hardcore_detail_density( - hard.get("hardcore_detail_density") or row.get("hardcore_detail_density") or options.get("hardcore_detail_density") - ) - hard_action = _hardcore_action_sentence( - hard_role_graph, - hard_item, - hard_source_composition, - hard_axis_values, - hard_detail_density, - route_metadata_policy.row_action_family(hard) or route_metadata_policy.row_action_family(row), - ) - hard_action = _pov_action_phrase( - hard_action, - pov_labels, - hard_role_graph, - hard_item, - hard_source_composition, - hard_axis_values, - hard_detail_density, - ) - hard_output_composition = _pov_composition_text(hard_composition, pov_labels) - same_soft_cast = options.get("softcore_cast") == "same_as_hardcore" - soft_output_composition = _pov_composition_text(soft.get("composition"), pov_labels if same_soft_cast else []) - if same_soft_cast and pov_labels: - soft_cast_presence = ( - "the woman is framed from the POV participant's first-person camera in a soft creator-teaser pose, " - "with the POV participant kept off-camera as the viewpoint and implied by camera position or foreground cues" - ) - else: - soft_cast_presence = ( - f"{_label_join(soft_labels)} share the frame in a soft creator-teaser pose" - if same_soft_cast - else "The image focuses on the woman alone" - ) - partner_styling = row.get("softcore_partner_styling") - if isinstance(partner_styling, dict): - outfits = partner_styling.get("outfits") - partner_outfit_text = "; ".join(_clean(item) for item in outfits if _clean(item)) if isinstance(outfits, list) else "" - partner_pose = _clean(partner_styling.get("pose")) - else: - partner_outfit_text = "" - partner_pose = "" - partner_outfit_text = _filter_pov_labeled_clauses(partner_outfit_text, pov_labels) - if pov_labels: - partner_pose = "" - partner_outfit_text = _natural_label_text(partner_outfit_text, soft_labels) - - soft_expression = "" - if not _expression_disabled(soft): - soft_expression_source = _filter_pov_labeled_clauses( - _clean(soft.get("character_expression_text")) or _clean(soft.get("expression")), - pov_labels, - ) - soft_expression = _natural_label_text( - soft_expression_source, - soft_labels, - ) - hard_expression = "" - if not _expression_disabled(hard): - hard_expression_source = _filter_pov_labeled_clauses( - _clean(hard.get("character_expression_text")) or _clean(hard.get("expression")), - pov_labels, - ) - hard_expression = _natural_label_text( - hard_expression_source, - hard_labels, - ) - soft_item = _clean(soft.get("item")) - soft_item_label = _clean(soft.get("softcore_item_prompt_label")) - soft_item_phrase = "" - if soft_item: - soft_item_phrase = f"body exposure: {soft_item}" if soft_item_label == "Body exposure" else f"wearing {soft_item}" - - soft_parts = [ - soft_cast_prose, - soft_cast_presence, - partner_outfit_text, - partner_pose, - _pov_camera_phrase(pov_labels, softcore=True) if same_soft_cast else "", - soft_item_phrase, - f"{soft.get('pose')}" if soft.get("pose") else "", - _expression_phrase(soft_expression), - f"in {soft.get('scene_text')}" if soft.get("scene_text") else "", - soft_camera_scene, - f"framed as {soft_output_composition}" if soft_output_composition else "", - soft_camera, - soft_style if detail_level != "concise" else "", - ] - hard_parts = [ - hard_action, - _pov_camera_phrase(pov_labels), - _natural_label_text( - _filter_pov_labeled_clauses(_natural_clothing_state(row.get("hardcore_clothing_state"), hard_action), pov_labels), - hard_labels, + return krea_pair_formatter.format_insta_pair( + krea_pair_formatter.KreaPairFormatRequest( + row=row, + detail_level=detail_level, + style_mode=style_mode, ), - hard_cast_prose, - f"set in {hard_scene}" if hard_scene else "", - hard_camera_scene, - _expression_phrase(hard_expression), - _composition_phrase(hard_output_composition, hard_action, detail_density=hard_detail_density), - hard_camera, - hard_style if detail_level != "concise" else "", - ] - return ( - _paragraph(soft_parts), - _combine_negative(row.get("softcore_negative_prompt")), - _paragraph(hard_parts), - _combine_negative(row.get("hardcore_negative_prompt")), + _krea_pair_format_dependencies(), ) diff --git a/krea_pair_formatter.py b/krea_pair_formatter.py new file mode 100644 index 0000000..eabd3e5 --- /dev/null +++ b/krea_pair_formatter.py @@ -0,0 +1,226 @@ +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any, Callable + + +@dataclass(frozen=True) +class KreaPairFormatRequest: + row: dict[str, Any] + detail_level: str + style_mode: str + + +@dataclass(frozen=True) +class KreaPairPrompts: + soft_prompt: str + soft_negative: str + hard_prompt: str + hard_negative: str + + def as_tuple(self) -> tuple[str, str, str, str]: + return self.soft_prompt, self.soft_negative, self.hard_prompt, self.hard_negative + + +@dataclass(frozen=True) +class KreaPairFormatDependencies: + clean: Callable[[Any], str] + prompt_cast_descriptors: Callable[[str], str] + pair_camera_phrase: Callable[[Any, Any, dict[str, Any]], str] + camera_scene_phrase: Callable[[dict[str, Any]], str] + style_phrase: Callable[[dict[str, Any], str], str] + sanitize_hardcore_environment_anchors: Callable[[Any], str] + sanitize_hardcore_axis_values: Callable[[Any], Any] + sanitize_scene_text_for_cast: Callable[[Any, list[str]], str] + normalize_hardcore_detail_density: Callable[[Any], str] + row_action_family: Callable[[Any], str] + hardcore_action_sentence: Callable[[str, str, str, Any, str, str], str] + pov_action_phrase: Callable[[str, list[str], str, str, str, Any, str], str] + pov_labels_from_value: Callable[[Any], list[str]] + merge_labels: Callable[..., list[str]] + cast_prose_omit: Callable[[str, list[str]], tuple[str, list[str]]] + label_join: Callable[[list[str]], str] + filter_pov_labeled_clauses: Callable[[Any, list[str]], str] + natural_label_text: Callable[[Any, list[str]], str] + expression_disabled: Callable[[dict[str, Any]], bool] + expression_phrase: Callable[[Any], str] + pov_camera_phrase: Callable[[list[str]], str] + pov_soft_camera_phrase: Callable[[list[str]], str] + pov_composition_text: Callable[[Any, list[str]], str] + natural_clothing_state: Callable[[Any, str], str] + composition_phrase: Callable[..., str] + paragraph: Callable[[list[str]], str] + combine_negative: Callable[..., str] + + +def format_insta_pair_result(request: KreaPairFormatRequest, deps: KreaPairFormatDependencies) -> KreaPairPrompts: + row = request.row + detail_level = request.detail_level + style_mode = request.style_mode + descriptor = deps.clean(row.get("shared_descriptor")) + cast_descriptors = row.get("shared_cast_descriptors") + if isinstance(cast_descriptors, list): + cast_descriptor_text = "; ".join(deps.clean(item) for item in cast_descriptors if deps.clean(item)) + else: + cast_descriptor_text = deps.clean(cast_descriptors) + cast_descriptor_text = deps.prompt_cast_descriptors(cast_descriptor_text) + soft = row.get("softcore_row") if isinstance(row.get("softcore_row"), dict) else {} + hard = row.get("hardcore_row") if isinstance(row.get("hardcore_row"), dict) else {} + soft_camera = deps.pair_camera_phrase(row.get("softcore_camera_directive"), row.get("softcore_camera_config"), soft) + hard_camera = deps.pair_camera_phrase(row.get("hardcore_camera_directive"), row.get("hardcore_camera_config"), hard) + soft_camera_scene = deps.camera_scene_phrase(soft) or deps.clean(row.get("softcore_camera_scene_directive")) + hard_camera_scene = deps.camera_scene_phrase(hard) or deps.clean(row.get("hardcore_camera_scene_directive")) + soft_style = deps.style_phrase(soft, style_mode) + hard_style = deps.style_phrase(hard, style_mode) + options = row.get("options") if isinstance(row.get("options"), dict) else {} + soft_level = deps.clean(options.get("softcore_level")).replace("_", " ") + hard_level = deps.clean(options.get("hardcore_level")).replace("_", " ") + same_room = options.get("continuity") == "same_creator_same_room" + hard_scene = soft.get("scene_text") if same_room and soft.get("scene_text") else hard.get("scene_text") + hard_composition = deps.sanitize_hardcore_environment_anchors(hard.get("composition")) + hard_source_composition = deps.sanitize_hardcore_environment_anchors(hard.get("source_composition") or hard_composition) + pov_labels = deps.merge_labels( + deps.pov_labels_from_value(row.get("pov_character_labels")), + deps.pov_labels_from_value(soft.get("pov_character_labels")), + deps.pov_labels_from_value(hard.get("pov_character_labels")), + ) + if pov_labels: + hard_camera = "" + if options.get("softcore_cast") == "same_as_hardcore": + soft_camera = "" + soft_cast_descriptor_text = ( + cast_descriptor_text + if options.get("softcore_cast") == "same_as_hardcore" + else f"Woman A: {descriptor}" + ) + soft_cast_prose, soft_labels = deps.cast_prose_omit( + soft_cast_descriptor_text, + pov_labels if options.get("softcore_cast") == "same_as_hardcore" else [], + ) + hard_cast_prose, hard_labels = deps.cast_prose_omit(cast_descriptor_text, pov_labels) + soft_labels = deps.merge_labels(soft_labels, pov_labels if options.get("softcore_cast") == "same_as_hardcore" else []) + hard_labels = deps.merge_labels(hard_labels, pov_labels) + hard_item = deps.sanitize_scene_text_for_cast( + deps.sanitize_hardcore_environment_anchors(hard.get("item")), + hard_labels, + ) + hard_role_graph = deps.sanitize_scene_text_for_cast( + deps.sanitize_hardcore_environment_anchors(hard.get("source_role_graph") or hard.get("role_graph")), + hard_labels, + ) + hard_item = deps.natural_label_text(hard_item, hard_labels) + hard_role_graph = deps.natural_label_text(hard_role_graph, hard_labels) + hard_axis_values = deps.sanitize_hardcore_axis_values(hard.get("item_axis_values")) + hard_detail_density = deps.normalize_hardcore_detail_density( + hard.get("hardcore_detail_density") or row.get("hardcore_detail_density") or options.get("hardcore_detail_density") + ) + hard_action = deps.hardcore_action_sentence( + hard_role_graph, + hard_item, + hard_source_composition, + hard_axis_values, + hard_detail_density, + deps.row_action_family(hard) or deps.row_action_family(row), + ) + hard_action = deps.pov_action_phrase( + hard_action, + pov_labels, + hard_role_graph, + hard_item, + hard_source_composition, + hard_axis_values, + hard_detail_density, + ) + hard_output_composition = deps.pov_composition_text(hard_composition, pov_labels) + same_soft_cast = options.get("softcore_cast") == "same_as_hardcore" + soft_output_composition = deps.pov_composition_text(soft.get("composition"), pov_labels if same_soft_cast else []) + if same_soft_cast and pov_labels: + soft_cast_presence = ( + "the woman is framed from the POV participant's first-person camera in a soft creator-teaser pose, " + "with the POV participant kept off-camera as the viewpoint and implied by camera position or foreground cues" + ) + else: + soft_cast_presence = ( + f"{deps.label_join(soft_labels)} share the frame in a soft creator-teaser pose" + if same_soft_cast + else "The image focuses on the woman alone" + ) + partner_styling = row.get("softcore_partner_styling") + if isinstance(partner_styling, dict): + outfits = partner_styling.get("outfits") + partner_outfit_text = "; ".join(deps.clean(item) for item in outfits if deps.clean(item)) if isinstance(outfits, list) else "" + partner_pose = deps.clean(partner_styling.get("pose")) + else: + partner_outfit_text = "" + partner_pose = "" + partner_outfit_text = deps.filter_pov_labeled_clauses(partner_outfit_text, pov_labels) + if pov_labels: + partner_pose = "" + partner_outfit_text = deps.natural_label_text(partner_outfit_text, soft_labels) + + soft_expression = "" + if not deps.expression_disabled(soft): + soft_expression_source = deps.filter_pov_labeled_clauses( + deps.clean(soft.get("character_expression_text")) or deps.clean(soft.get("expression")), + pov_labels, + ) + soft_expression = deps.natural_label_text( + soft_expression_source, + soft_labels, + ) + hard_expression = "" + if not deps.expression_disabled(hard): + hard_expression_source = deps.filter_pov_labeled_clauses( + deps.clean(hard.get("character_expression_text")) or deps.clean(hard.get("expression")), + pov_labels, + ) + hard_expression = deps.natural_label_text( + hard_expression_source, + hard_labels, + ) + soft_item = deps.clean(soft.get("item")) + soft_item_label = deps.clean(soft.get("softcore_item_prompt_label")) + soft_item_phrase = "" + if soft_item: + soft_item_phrase = f"body exposure: {soft_item}" if soft_item_label == "Body exposure" else f"wearing {soft_item}" + + soft_parts = [ + soft_cast_prose, + soft_cast_presence, + partner_outfit_text, + partner_pose, + deps.pov_soft_camera_phrase(pov_labels) if same_soft_cast else "", + soft_item_phrase, + f"{soft.get('pose')}" if soft.get("pose") else "", + deps.expression_phrase(soft_expression), + f"in {soft.get('scene_text')}" if soft.get("scene_text") else "", + soft_camera_scene, + f"framed as {soft_output_composition}" if soft_output_composition else "", + soft_camera, + soft_style if detail_level != "concise" else "", + ] + hard_parts = [ + hard_action, + deps.pov_camera_phrase(pov_labels), + deps.natural_label_text( + deps.filter_pov_labeled_clauses(deps.natural_clothing_state(row.get("hardcore_clothing_state"), hard_action), pov_labels), + hard_labels, + ), + hard_cast_prose, + f"set in {hard_scene}" if hard_scene else "", + hard_camera_scene, + deps.expression_phrase(hard_expression), + deps.composition_phrase(hard_output_composition, hard_action, detail_density=hard_detail_density), + hard_camera, + hard_style if detail_level != "concise" else "", + ] + return KreaPairPrompts( + soft_prompt=deps.paragraph(soft_parts), + soft_negative=deps.combine_negative(row.get("softcore_negative_prompt")), + hard_prompt=deps.paragraph(hard_parts), + hard_negative=deps.combine_negative(row.get("hardcore_negative_prompt")), + ) + + +def format_insta_pair(request: KreaPairFormatRequest, deps: KreaPairFormatDependencies) -> tuple[str, str, str, str]: + return format_insta_pair_result(request, deps).as_tuple() diff --git a/tools/prompt_smoke.py b/tools/prompt_smoke.py index 7569753..e50e0d9 100644 --- a/tools/prompt_smoke.py +++ b/tools/prompt_smoke.py @@ -43,6 +43,7 @@ import generation_profile_config # noqa: E402 import index_switch_policy # noqa: E402 import krea_cast # noqa: E402 import krea_formatter # noqa: E402 +import krea_pair_formatter # noqa: E402 import location_config # noqa: E402 import loop_nodes # noqa: E402 import pair_builder # noqa: E402 @@ -3196,6 +3197,15 @@ def smoke_krea_pair_clothing_state() -> None: hardcore_position_config=_action_filter("penetration_only"), ) _expect_pair(pair, "krea_pair_clothing_state") + typed_route = krea_pair_formatter.format_insta_pair_result( + krea_pair_formatter.KreaPairFormatRequest(pair, "balanced", "preserve"), + krea_formatter._krea_pair_format_dependencies(), + ) + legacy_route = krea_formatter._insta_pair_to_krea(pair, "balanced", "preserve") + _expect( + typed_route.as_tuple() == legacy_route, + "Typed Krea pair formatter route should match legacy wrapper output", + ) krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(pair), target="hardcore") prompt = _expect_text("krea_pair_clothing_state.krea_prompt", krea.get("krea_prompt"), 60) lower = prompt.lower()