From eff7f57d77ab9965498b030cf505b103ede05244 Mon Sep 17 00:00:00 2001 From: BRADSEC <7948876+bradsec@users.noreply.github.com> Date: Sat, 20 Jun 2026 19:20:02 +1000 Subject: [PATCH] refactor: rename random_person_node.py to nodes.py; default optional flair categories (facial_hair, makeup, accessories, expression, face_feature, skin_texture) to off; bump 1.0.2 --- README.md | 8 ++++++++ __init__.py | 4 ++-- random_person_node.py => nodes.py | 15 +++++++++++++-- pyproject.toml | 2 +- test_random_person.py | 11 ++++++++++- 5 files changed, 34 insertions(+), 6 deletions(-) rename random_person_node.py => nodes.py (97%) diff --git a/README.md b/README.md index 94c7123..14b15ae 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,14 @@ Every attribute category (nationality, complexion, skin texture, eyes, face shap > **Tip: turning off attributes:** Set any category's mode to `off` to remove it from the output completely. For example, if you don't want a body type in the description, set `body_type_mode` to `off`. +#### Default modes + +Core identity categories (nationality, complexion, eyes, face shape, nose shape, mouth shape, hair colour/style/length, body type, plus age and sex) default to `random`. The optional "flair" categories default to `off` so the base person stays clean and realistic, and you opt them in per category: + +`skin_texture`, `face_feature`, `facial_hair`, `expression`, `accessories`, `makeup` + +The example outputs above have several of these enabled to show the full range. + #### Allow List: Custom Values The allow_list field accepts comma-separated values. These can be: diff --git a/__init__.py b/__init__.py index 6506201..d4ce22b 100644 --- a/__init__.py +++ b/__init__.py @@ -5,14 +5,14 @@ comfy_entrypoint for builds whose loader prefers the V3 schema API. WEB_DIRECTOR ships the sex-aware widget extension. """ -from .random_person_node import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS +from .nodes import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS WEB_DIRECTORY = "./js" __all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS", "WEB_DIRECTORY"] try: - from .random_person_node import comfy_entrypoint # noqa: F401 + from .nodes import comfy_entrypoint # noqa: F401 __all__.append("comfy_entrypoint") except ImportError: pass diff --git a/random_person_node.py b/nodes.py similarity index 97% rename from random_person_node.py rename to nodes.py index 7efb10b..0250c0e 100644 --- a/random_person_node.py +++ b/nodes.py @@ -121,6 +121,17 @@ CATEGORY_SPECS = [ SEX_GATED = {"hair_color", "hair_style", "hair_length", "body_type", "face_shape", "nose_shape", "mouth_shape", "face_feature", "facial_hair", "makeup"} +# Optional "flair" categories default to mode "off" so the base description stays +# clean and realistic; the user opts them in per category. Core identity +# categories (nationality, complexion, eyes, face/nose/mouth shape, hair, body) +# default to "random". +DEFAULT_OFF = {"skin_texture", "face_feature", "facial_hair", + "expression", "accessories", "makeup"} + + +def _mode_default(key): + return "off" if key in DEFAULT_OFF else "random" + # Tooltips (shared by both APIs). _SEED_TIP = "Seed for reproducibility. With randomize off, the same seed reproduces the same person." _RAND_TIP = "On: pick a new random person every run, ignoring the seed widget. Off: use the seed." @@ -399,7 +410,7 @@ class RandomPersonNode: "sex": (SEX_MODES, {"default": "random", "tooltip": _SEX_TIP}), } for key, fixed_labels, placeholder in CATEGORY_SPECS: - required[f"{key}_mode"] = (MODES, {"default": "random", "tooltip": _MODE_TIP}) + required[f"{key}_mode"] = (MODES, {"default": _mode_default(key), "tooltip": _MODE_TIP}) required[f"{key}_allow_list"] = ("STRING", {"default": "", "multiline": False, "placeholder": placeholder, "tooltip": _ALLOW_TIP}) required[f"{key}_fixed"] = (fixed_labels, {"default": "(none)", "tooltip": _FIXED_TIP}) @@ -442,7 +453,7 @@ try: ] for key, fixed_labels, placeholder in CATEGORY_SPECS: inputs += [ - io.Combo.Input(f"{key}_mode", options=MODES, default="random", tooltip=_MODE_TIP), + io.Combo.Input(f"{key}_mode", options=MODES, default=_mode_default(key), tooltip=_MODE_TIP), io.String.Input(f"{key}_allow_list", default="", multiline=False, placeholder=placeholder, tooltip=_ALLOW_TIP), io.Combo.Input(f"{key}_fixed", options=fixed_labels, default="(none)", tooltip=_FIXED_TIP), diff --git a/pyproject.toml b/pyproject.toml index e0a229a..b858fb5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "comfyui_randomperson" description = "Generate randomised, structured physical person descriptions for image-generation prompts. Sex-aware traits, per-category random/allow_list/fixed/off modes, age ranges, and individual output pins. Located under Add Node > utils. Node name: Random Person Description." -version = "1.0.1" +version = "1.0.2" license = { file = "LICENSE" } requires-python = ">=3.10" dependencies = [] diff --git a/test_random_person.py b/test_random_person.py index 7ecc306..e0cf591 100644 --- a/test_random_person.py +++ b/test_random_person.py @@ -6,7 +6,7 @@ Run: python -m unittest test_random_person -v import unittest -import random_person_node as core +import nodes as core def cat_args(**overrides): @@ -65,6 +65,15 @@ class TestSelection(unittest.TestCase): def test_sex_resolved_from_random(self): self.assertIn(run(sex="random")[1], ("male", "female")) + def test_flair_categories_default_off(self): + # Optional categories must ship with mode default "off"; identity + # categories must default "random". + req = core.RandomPersonNode.INPUT_TYPES()["required"] + for key in core.DEFAULT_OFF: + self.assertEqual(req[f"{key}_mode"][1]["default"], "off", key) + for key in ("nationality", "complexion", "eyes", "hair_color", "body_type"): + self.assertEqual(req[f"{key}_mode"][1]["default"], "random", key) + def test_female_never_grows_facial_hair(self): for seed in range(40): self.assertEqual(run(seed=seed, sex="female")[7], "") # facial_hair pin empty