feat: per-node full_*_description first pin labels; README AIO height note
This commit is contained in:
@@ -17,7 +17,9 @@ The nodes appear in the **Add Node** menu under **Random Person**:
|
||||
- **Random Person: Body** - build, shoulders, chest, bust size and shape.
|
||||
- **Random Person: Style** - accessories, makeup, clothing, and footwear.
|
||||
|
||||
The segment nodes are independent generators: each has its own seed and sex and emits its group's fragment. Concatenate the `description` outputs with any string node to assemble a full prompt.
|
||||
The AIO node exposes every category at once, so it is a very tall node. You do not have to use it: if you only need part of a person, or want a shorter, tidier graph, use the relevant segment node on its own. The segment nodes are independent generators - each has its own seed and sex and emits only its group's fragment. Use one alone, or wire several together (set the same `sex` on each) and concatenate their full description outputs with any string node to assemble a complete prompt.
|
||||
|
||||
Each node's first output pin is its full description, named per node so it is clear where it came from: `full_description` (AIO), `full_identity_description`, `full_face_description`, `full_hair_description`, `full_body_description`, `full_style_description`.
|
||||
|
||||
**Example output (male):**
|
||||
```
|
||||
@@ -49,7 +51,7 @@ The nodes appear in the **Add Node** menu under **Random Person**.
|
||||
|
||||
| Pin | Contents | Example |
|
||||
|---|---|---|
|
||||
| `description` | Full comma-separated description string | `43 year old Russian female, angular face, ...` |
|
||||
| `full_description` | Full comma-separated description string (the AIO node's first pin; segment nodes name theirs `full_<group>_description`) | `43 year old Russian female, angular face, ...` |
|
||||
| `sex` | Resolved sex | `female` |
|
||||
| `age` | Age as a plain number | `43` |
|
||||
| `nationality` | Nationality descriptor | `Russian` |
|
||||
|
||||
@@ -499,17 +499,36 @@ NODE_SPECS = [
|
||||
|
||||
_NODE_DESCRIPTION = "Generate a randomised, structured physical person description for image prompts."
|
||||
|
||||
# Per-node display name for the first ("description") output pin. The internal
|
||||
# lookup key stays "description"; only the shown pin label changes, so it is
|
||||
# clear which node a full description came from.
|
||||
_FIRST_PIN_LABEL = {
|
||||
"RandomPersonNode": "full_description",
|
||||
"RandomPersonIdentity": "full_identity_description",
|
||||
"RandomPersonFace": "full_face_description",
|
||||
"RandomPersonHair": "full_hair_description",
|
||||
"RandomPersonBody": "full_body_description",
|
||||
"RandomPersonStyle": "full_style_description",
|
||||
}
|
||||
|
||||
|
||||
def _display_names(node_id, output_names):
|
||||
"""Output names as shown in the UI: first pin relabelled per node, rest unchanged."""
|
||||
first = _FIRST_PIN_LABEL.get(node_id, output_names[0])
|
||||
return (first,) + tuple(output_names[1:])
|
||||
|
||||
|
||||
def make_v1_node(node_id, display_name, keys, include_age, output_names):
|
||||
"""Build a ComfyUI V1 node class exposing only `keys` and returning `output_names`."""
|
||||
key_set = set(keys)
|
||||
display = _display_names(node_id, output_names)
|
||||
|
||||
class _RandomPersonV1:
|
||||
CATEGORY = "Random Person"
|
||||
FUNCTION = "generate"
|
||||
OUTPUT_NODE = False
|
||||
DESCRIPTION = _NODE_DESCRIPTION
|
||||
RETURN_NAMES = output_names
|
||||
RETURN_NAMES = display
|
||||
RETURN_TYPES = tuple("INT" if n == "seed" else "STRING" for n in output_names)
|
||||
|
||||
@classmethod
|
||||
@@ -575,6 +594,7 @@ try:
|
||||
|
||||
def _make_v3_node(node_id, display_name, keys, include_age, output_names):
|
||||
key_set = set(keys)
|
||||
display_pins = _display_names(node_id, output_names)
|
||||
|
||||
class _RandomPersonV3(io.ComfyNode):
|
||||
@classmethod
|
||||
@@ -604,7 +624,7 @@ try:
|
||||
io.String.Input("extra_attributes", default="", multiline=True,
|
||||
placeholder="Add any extra details to pass into the description, e.g. wearing a red scarf, holding a coffee, tattoo on left arm", tooltip=_EXTRA_TIP))
|
||||
outputs = []
|
||||
for name in output_names:
|
||||
for name in display_pins:
|
||||
if name == "seed":
|
||||
outputs.append(io.Int.Output(id="seed", display_name="seed", tooltip=_SEED_TIP))
|
||||
else:
|
||||
|
||||
+17
-2
@@ -290,6 +290,22 @@ class TestSegmentNodes(unittest.TestCase):
|
||||
for cls in core.NODE_CLASS_MAPPINGS.values():
|
||||
self.assertEqual(cls.CATEGORY, "Random Person")
|
||||
|
||||
def test_first_pin_relabelled_per_node(self):
|
||||
# Display label of the first pin is node-specific; internal value key stays "description".
|
||||
expect = {
|
||||
"RandomPersonNode": "full_description",
|
||||
"RandomPersonIdentity": "full_identity_description",
|
||||
"RandomPersonFace": "full_face_description",
|
||||
"RandomPersonHair": "full_hair_description",
|
||||
"RandomPersonBody": "full_body_description",
|
||||
"RandomPersonStyle": "full_style_description",
|
||||
}
|
||||
for node_id, label in expect.items():
|
||||
self.assertEqual(core.NODE_CLASS_MAPPINGS[node_id].RETURN_NAMES[0], label, node_id)
|
||||
# Module RETURN_NAMES (the value-lookup keys) is unchanged, so the first
|
||||
# pin still resolves to the description value.
|
||||
self.assertEqual(core.RETURN_NAMES[0], "description")
|
||||
|
||||
def test_identity_exposes_only_its_widgets(self):
|
||||
req = self._node("RandomPersonIdentity").INPUT_TYPES()["required"]
|
||||
self.assertIn("nationality_mode", req)
|
||||
@@ -314,8 +330,7 @@ class TestSegmentNodes(unittest.TestCase):
|
||||
elif name.endswith("_fixed"):
|
||||
kwargs[name] = "(none)"
|
||||
out = cls().generate(seed=3, randomize=False, sex="female", **kwargs)
|
||||
names = cls.RETURN_NAMES
|
||||
desc = out[names.index("description")]
|
||||
desc = out[0] # first pin is always the full description
|
||||
self.assertNotIn("build", desc) # body excluded
|
||||
self.assertNotIn("hair", desc) # hair excluded
|
||||
self.assertNotIn("year old", desc) # age excluded
|
||||
|
||||
Reference in New Issue
Block a user