diff --git a/hardcore_position_config.py b/hardcore_position_config.py index f4aaee5..7c0adc4 100644 --- a/hardcore_position_config.py +++ b/hardcore_position_config.py @@ -483,7 +483,11 @@ def build_hardcore_position_pool_json( ) -> str: base = parse_hardcore_position_config(hardcore_position_config) if combine_mode == "replace": + restore_axes = normalize_restore_prompt_axes(base.get("restore_prompt_axes")) + relax_non_pose_axis_conflicts = bool(base.get("relax_non_pose_axis_conflicts")) base = {**empty_hardcore_position_config(), "enabled": True} + base["restore_prompt_axes"] = restore_axes + base["relax_non_pose_axis_conflicts"] = relax_non_pose_axis_conflicts else: base["enabled"] = True base["family"] = normalize_hardcore_position_family(family, base.get("family", "any")) diff --git a/node_hardcore_position.py b/node_hardcore_position.py index c95f306..b4d2a44 100644 --- a/node_hardcore_position.py +++ b/node_hardcore_position.py @@ -351,13 +351,15 @@ class SxCPKrea2POVPromptRestore: def INPUT_TYPES(cls): return { "required": { - "hardcore_position_config": (SXCP_HARDCORE_POSITION_CONFIG,), "restore_clothing_detail": ("BOOLEAN", {"default": True}), "restore_face_expression_detail": ("BOOLEAN", {"default": True}), "restore_body_touch_detail": ("BOOLEAN", {"default": False}), "restore_camera_presentation_detail": ("BOOLEAN", {"default": False}), "relax_non_pose_axis_conflicts": ("BOOLEAN", {"default": True}), - } + }, + "optional": { + "hardcore_position_config": (SXCP_HARDCORE_POSITION_CONFIG,), + }, } RETURN_TYPES = (SXCP_HARDCORE_POSITION_CONFIG, "STRING") @@ -367,12 +369,12 @@ class SxCPKrea2POVPromptRestore: def build( self, - hardcore_position_config, restore_clothing_detail=True, restore_face_expression_detail=True, restore_body_touch_detail=False, restore_camera_presentation_detail=False, relax_non_pose_axis_conflicts=True, + hardcore_position_config="", ): config = parse_hardcore_position_config(hardcore_position_config) axes: list[str] = [] @@ -392,6 +394,8 @@ class SxCPKrea2POVPromptRestore: axes.extend(self.CAMERA_PRESENTATION_AXES) config["allow_interaction"] = True config["restore_prompt_axes"] = normalize_restore_prompt_axes(axes) + if config["restore_prompt_axes"]: + config["enabled"] = True config["relax_non_pose_axis_conflicts"] = bool(relax_non_pose_axis_conflicts) config["summary"] = hardcore_position_summary(config) return json.dumps(config, ensure_ascii=True, sort_keys=True), str(config["summary"]) diff --git a/tools/prompt_smoke.py b/tools/prompt_smoke.py index 2a01cd9..fd801f8 100644 --- a/tools/prompt_smoke.py +++ b/tools/prompt_smoke.py @@ -11353,6 +11353,15 @@ def smoke_node_hardcore_position_registration() -> None: restore_node = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPKrea2POVPromptRestore"] restore_inputs = restore_node.INPUT_TYPES().get("required") or {} + restore_optional = restore_node.INPUT_TYPES().get("optional") or {} + _expect( + "hardcore_position_config" not in restore_inputs, + "Krea2 POV Prompt Restore should not require an incoming position config connection", + ) + _expect( + "hardcore_position_config" in restore_optional, + "Krea2 POV Prompt Restore should expose incoming position config as an optional connection", + ) for key in ( "restore_clothing_detail", "restore_face_expression_detail", @@ -11361,13 +11370,68 @@ def smoke_node_hardcore_position_registration() -> None: "relax_non_pose_axis_conflicts", ): _expect(key in restore_inputs, f"Krea2 POV Prompt Restore lost input {key}") + standalone_restore_config, standalone_restore_summary = restore_node().build( + True, + True, + False, + False, + True, + "", + ) + parsed_standalone_restore = json.loads(standalone_restore_config) + _expect( + parsed_standalone_restore.get("restore_prompt_axes") == [ + "clothing_detail", + "face_detail", + "expression_detail", + "mouth_detail", + "reaction_detail", + ], + "Krea2 POV Prompt Restore should emit restore axes without an incoming connection", + ) + _expect( + "restore_axes=clothing_detail,face_detail" in standalone_restore_summary, + "Krea2 POV Prompt Restore standalone summary lost restored axes", + ) + pre_restore_config, _pre_restore_summary = restore_node().build( + True, + True, + False, + False, + True, + "", + ) + pre_restored_top_config, _pre_restored_top_keys, _pre_restored_top_positions, _, _, _ = oral_filter().build( + "replace", + pre_restore_config, + include_blowjob_top_down_vertical_shaft=True, + ) + parsed_pre_restored_top = json.loads(pre_restored_top_config) + _expect( + parsed_pre_restored_top.get("krea2_variant_keys") == ["pov_blowjob_top_down_vertical_shaft"], + "Restore-before-filter should keep the selected top-view atlas variant metadata", + ) + _expect( + parsed_pre_restored_top.get("restore_prompt_axes") == [ + "clothing_detail", + "face_detail", + "expression_detail", + "mouth_detail", + "reaction_detail", + ], + "Restore-before-filter should preserve restore axes through replace-mode pose filtering", + ) + _expect( + parsed_pre_restored_top.get("relax_non_pose_axis_conflicts") is True, + "Restore-before-filter should preserve non-pose conflict relaxation through replace-mode pose filtering", + ) restored_config, restored_summary = restore_node().build( + True, + True, + False, + False, + True, doggy_config, - True, - True, - False, - False, - True, ) parsed_restored_config = json.loads(restored_config) _expect(