Add typed pair route contracts
This commit is contained in:
@@ -45,8 +45,10 @@ import krea_cast # noqa: E402
|
||||
import krea_formatter # noqa: E402
|
||||
import location_config # noqa: E402
|
||||
import loop_nodes # noqa: E402
|
||||
import pair_camera # noqa: E402
|
||||
import pair_cast # noqa: E402
|
||||
import pair_clothing # noqa: E402
|
||||
import pair_rows # noqa: E402
|
||||
import prompt_builder as pb # noqa: E402
|
||||
import pov_policy # noqa: E402
|
||||
import row_normalization # noqa: E402
|
||||
@@ -2922,6 +2924,148 @@ def smoke_pair_options_policy() -> None:
|
||||
_expect(pb._insta_of_softcore_category("social_tease") == ("Casual clothes", "Casual clothes / Smart casual"), "softcore category mapping changed")
|
||||
|
||||
|
||||
def smoke_pair_route_policy() -> None:
|
||||
def _fake_build_prompt(**kwargs: Any) -> dict[str, Any]:
|
||||
is_hard = kwargs.get("category") == "Hardcore sexual poses"
|
||||
return {
|
||||
"category": kwargs.get("category"),
|
||||
"subcategory": kwargs.get("subcategory"),
|
||||
"scene_text": "shared test room",
|
||||
"source_scene_text": "",
|
||||
"composition": "centered test composition",
|
||||
"source_composition": "",
|
||||
"expression": "steady look",
|
||||
"role_graph": "test role graph",
|
||||
"item": "test item",
|
||||
"positive_suffix": "test positive suffix",
|
||||
"negative_prompt": "test negative",
|
||||
"prompt": "test hard prompt" if is_hard else "test soft prompt",
|
||||
"caption": "test hard caption" if is_hard else "test soft caption",
|
||||
}
|
||||
|
||||
pair_options_data = {
|
||||
"softcore_cast": "solo",
|
||||
"softcore_expression_enabled": True,
|
||||
"softcore_expression_intensity": 0.45,
|
||||
"hardcore_expression_enabled": True,
|
||||
"hardcore_expression_intensity": 0.85,
|
||||
"hardcore_detail_density": "balanced",
|
||||
}
|
||||
pair_rows_kwargs: dict[str, Any] = {
|
||||
"row_number": 1,
|
||||
"start_index": 1,
|
||||
"seed": 10,
|
||||
"active_trigger": Trigger,
|
||||
"parsed_seed_config": {},
|
||||
"options": pair_options_data,
|
||||
"ethnicity": "any",
|
||||
"figure": "random",
|
||||
"no_plus_women": False,
|
||||
"no_black": False,
|
||||
"character_profile": "",
|
||||
"character_cast": "",
|
||||
"character_slot_map": {},
|
||||
"pov_character_labels": [],
|
||||
"hard_women_count": 1,
|
||||
"hard_men_count": 1,
|
||||
"soft_category": "Casual clothes",
|
||||
"soft_subcategory": "Casual clothes / Smart casual",
|
||||
"softcore_level_key": "social_tease",
|
||||
"hardcore_random_subcategory": pb.RANDOM_SUBCATEGORY,
|
||||
"hardcore_position_config": "",
|
||||
"location_config": "",
|
||||
"composition_config": "",
|
||||
"build_prompt": _fake_build_prompt,
|
||||
"axis_rng": lambda _config, _axis, seed_value, row_value: random.Random(seed_value + row_value),
|
||||
"cast_expression_intensity_override": lambda value, _slots, _women, _men, _phase: (value, "input"),
|
||||
"context_from_character_slot": lambda *_args, **_kwargs: {},
|
||||
"apply_character_context_to_row": lambda row, context: {**row, **context},
|
||||
"disable_row_expression": lambda row, source: {**row, "expression_disabled": True, "expression_intensity_source": source},
|
||||
"slot_softcore_outfit": lambda _slot, _rng: "",
|
||||
"softcore_outfit": lambda _rng, _level: "test soft outfit",
|
||||
"softcore_pose": lambda _rng, _level: "test soft pose",
|
||||
"softcore_item_prompt_label": lambda _level: "Softcore test outfit",
|
||||
"pov_prompt_directive": lambda labels: "POV directive" if labels else "",
|
||||
"pov_composition_prompt": lambda composition, labels: f"{composition} for {','.join(labels)}" if labels else str(composition),
|
||||
}
|
||||
rows_route = pair_rows.build_insta_pair_rows_result(**pair_rows_kwargs)
|
||||
rows_legacy = pair_rows.build_insta_pair_rows(**pair_rows_kwargs)
|
||||
_expect(rows_route.soft_row == rows_legacy["soft_row"], "Typed pair row route should match legacy soft row")
|
||||
_expect(rows_route.hard_row == rows_legacy["hard_row"], "Typed pair row route should match legacy hard row")
|
||||
_expect(
|
||||
rows_route.hard_content_rng.getstate() == rows_legacy["hard_content_rng"].getstate(),
|
||||
"Typed pair row route should match legacy hard content RNG state",
|
||||
)
|
||||
_expect(rows_route.soft_row["item"] == "test soft outfit", "Typed pair row route lost soft outfit override")
|
||||
_expect(rows_route.hard_row["hardcore_detail_density"] == "balanced", "Typed pair row route lost hard density")
|
||||
|
||||
camera_options = {
|
||||
"hardcore_camera_mode": "same_as_softcore",
|
||||
"softcore_camera_mode": "standard",
|
||||
"camera_detail": "compact",
|
||||
"softcore_cast": "same_as_hardcore",
|
||||
"continuity": "same_creator_same_room",
|
||||
}
|
||||
|
||||
def _camera_config_with_mode(source: Any, mode: str) -> dict[str, Any]:
|
||||
parsed = dict(source or {})
|
||||
parsed["camera_mode"] = mode
|
||||
return parsed
|
||||
|
||||
def _camera_directive(config: dict[str, Any]) -> tuple[str, dict[str, Any]]:
|
||||
return f"{config.get('camera_mode')} camera", config
|
||||
|
||||
def _camera_rows() -> tuple[dict[str, Any], dict[str, Any]]:
|
||||
return (
|
||||
{"scene_text": "soft room", "composition": "soft composition"},
|
||||
{"scene_text": "hard room", "composition": "hard composition"},
|
||||
)
|
||||
|
||||
camera_common = {
|
||||
"options": camera_options,
|
||||
"camera_config": {"base": "camera"},
|
||||
"softcore_camera_config": None,
|
||||
"hardcore_camera_config": None,
|
||||
"hard_women_count": 1,
|
||||
"hard_men_count": 1,
|
||||
"pov_character_labels": [],
|
||||
"camera_detail_choices": ("compact",),
|
||||
"camera_config_with_mode": _camera_config_with_mode,
|
||||
"camera_directive": _camera_directive,
|
||||
"apply_contextual_composition": lambda row, subject_kind: {**row, "subject_kind": subject_kind},
|
||||
"contextual_composition_prompt": lambda scene, composition, subject_kind: f"{subject_kind}: {scene}: {composition}",
|
||||
"composition_prompt": lambda composition: f"Framed as {composition}",
|
||||
"camera_scene_directive_for_context": lambda _scene, _composition, config, _pov, subject: (f"{subject} scene directive", config),
|
||||
}
|
||||
soft_row, hard_row = _camera_rows()
|
||||
camera_route = pair_camera.resolve_insta_pair_camera_result(soft_row=soft_row, hard_row=hard_row, **camera_common)
|
||||
soft_row, hard_row = _camera_rows()
|
||||
camera_legacy = pair_camera.resolve_insta_pair_camera(soft_row=soft_row, hard_row=hard_row, **camera_common)
|
||||
_expect(camera_route.as_dict() == camera_legacy, "Typed pair camera route should match legacy dict route")
|
||||
_expect(camera_route.hard_scene == "soft room", "Typed pair camera route lost same-room continuity")
|
||||
_expect(camera_route.hard_camera_sentence.startswith("Camera control:"), "Typed pair camera route lost hard camera sentence")
|
||||
|
||||
clothing_common = {
|
||||
"hard_row": {
|
||||
"role_graph": "the man thrusts his penis into the woman",
|
||||
"item": "penetrative test item",
|
||||
},
|
||||
"mode": "explicit_nude",
|
||||
"softcore_outfit": "test lingerie",
|
||||
"character_hardcore_clothing_entries": [],
|
||||
"men_count": 0,
|
||||
"pov_labels": [],
|
||||
"rng": random.Random(1),
|
||||
"continuity_map": pb.INSTA_OF_HARDCORE_CLOTHING_CONTINUITY,
|
||||
"choose": lambda _rng, pool: pool[0],
|
||||
}
|
||||
clothing_route = pair_clothing.resolve_hardcore_pair_clothing_result(**clothing_common)
|
||||
clothing_legacy = pair_clothing.resolve_hardcore_pair_clothing(**clothing_common)
|
||||
_expect(clothing_route.as_dict() == clothing_legacy, "Typed pair clothing route should match legacy dict route")
|
||||
_expect(clothing_route.woman_access == "lower", "Typed pair clothing route lost lower-access detection")
|
||||
_expect(clothing_route.requires_body_exposure_scene is True, "Typed pair clothing route lost exposure-scene flag")
|
||||
|
||||
|
||||
def _expect_pair(pair: dict[str, Any], name: str) -> None:
|
||||
_expect(pair.get("mode") == "Insta/OF", f"{name}.mode should be Insta/OF")
|
||||
_expect_row_base(pair.get("softcore_row") or {}, f"{name}.softcore_row")
|
||||
@@ -4823,6 +4967,7 @@ SMOKE_CASES: list[tuple[str, Callable[[], None]]] = [
|
||||
("hardcore_category_routes", smoke_hardcore_category_routes),
|
||||
("krea_close_foreplay_route", smoke_krea_close_foreplay_route),
|
||||
("pair_options_policy", smoke_pair_options_policy),
|
||||
("pair_route_policy", smoke_pair_route_policy),
|
||||
("insta_pair_same_cast", smoke_insta_pair),
|
||||
("krea_pair_clothing_state", smoke_krea_pair_clothing_state),
|
||||
("insta_pair_pov_man", smoke_insta_pair_pov),
|
||||
|
||||
Reference in New Issue
Block a user