Merge pull request #4 from Limbicnation/fix/instance-id-validation

fix(validation): instance_id type + combo field validation for legacy…
This commit is contained in:
Gero Doll
2026-04-22 04:08:08 +02:00
committed by GitHub
3 changed files with 94 additions and 28 deletions
+4
View File
@@ -73,6 +73,10 @@ When processing video frames through ComfyUI, face detection bboxes can jitter f
## Changelog
### v2.1.3
- **FIX**: `VALIDATE_INPUTS` now includes required combo fields (`aspect_ratio`, `output_mode`, `classifier_type`) in its signature, forcing ComfyUI to delegate their validation to our method instead of doing strict framework-level "Value not in list" checks. Legacy workflows with misaligned positional `widgets_values` (e.g. `'auto'` landing on `output_mode`, `0` landing on `classifier_type`) are now caught and replaced with defaults.
- **FIX**: Added defensive combo sanitization in both v1 and v3 execute methods — invalid combo values are replaced with defaults at runtime as a safety net.
### v2.1.2
- **FIX**: `instance_id` input type changed from INT to STRING. Legacy workflows pass `instance_id="default"` (string) from old v1 nodes — ComfyUI's `validate_inputs` runs before `execute()`, so `_coerce_int` never fires. Since `instance_id` is only a dict key, STRING is the correct type.
+89 -27
View File
@@ -1,7 +1,20 @@
"""
FaceDetectionNode — Optimized for H100 Cloud & LTX-Video Avatar Pipelines
========================================================================
Version: 2.1.2
Version: 2.1.3
CHANGELOG from v2.1.2 → v2.1.3:
1. FIX: VALIDATE_INPUTS now includes required combo fields (aspect_ratio,
output_mode, classifier_type) in its signature. This causes ComfyUI to
delegate their validation to our method instead of doing strict
framework-level "Value not in list" checks. Legacy workflows with
misaligned positional widgets_values (e.g. 'auto' landing on output_mode,
0 landing on classifier_type) are now caught and replaced with defaults.
2. Added _VALID_OUTPUT_MODES, _VALID_CLASSIFIER_TYPES, _VALID_ASPECT_RATIOS
tuples and their defaults for consistent validation across VALIDATE_INPUTS
and both v1/v3 execute methods.
3. Added defensive combo sanitization in v1 and v3 execute methods — invalid
values are replaced with defaults at runtime as a safety net.
CHANGELOG from v2.1.1 → v2.1.2:
1. FIX: instance_id input type changed from INT to STRING. Legacy workflows
@@ -95,6 +108,12 @@ ASPECT_RATIOS: dict[str, Optional[float]] = {
# ──────────────────────────────────────────────────────────────────────────────
_VALID_FACE_OUTPUT_FORMATS = ("strip", "individual")
_DEFAULT_FACE_OUTPUT_FORMAT = "strip"
_VALID_OUTPUT_MODES = ("largest_face", "all_faces")
_DEFAULT_OUTPUT_MODE = "largest_face"
_VALID_CLASSIFIER_TYPES = ("default", "alternative")
_DEFAULT_CLASSIFIER_TYPE = "default"
_VALID_ASPECT_RATIOS = ("auto", "1:1", "9:16", "16:9", "4:3")
_DEFAULT_ASPECT_RATIO = "auto"
# Default values for INT inputs — used by _coerce_int when legacy workflows
# pass string values (e.g. "default" from classifier_type) into INT slots.
@@ -468,6 +487,21 @@ if COMFY_V3:
instance_id = str(instance_id) if instance_id is not None else "0"
padding = _coerce_int(padding, "padding", 0)
# Defensive: sanitize combo fields that may have wrong values from
# legacy workflows with misaligned positional widgets_values
if aspect_ratio not in _VALID_ASPECT_RATIOS:
logger.warning("FaceDetectionNode: aspect_ratio='%s' invalid, using '%s'",
aspect_ratio, _DEFAULT_ASPECT_RATIO)
aspect_ratio = _DEFAULT_ASPECT_RATIO
if output_mode not in _VALID_OUTPUT_MODES:
logger.warning("FaceDetectionNode: output_mode='%s' invalid, using '%s'",
output_mode, _DEFAULT_OUTPUT_MODE)
output_mode = _DEFAULT_OUTPUT_MODE
if classifier_type not in _VALID_CLASSIFIER_TYPES:
logger.warning("FaceDetectionNode: classifier_type='%s' invalid, using '%s'",
classifier_type, _DEFAULT_CLASSIFIER_TYPE)
classifier_type = _DEFAULT_CLASSIFIER_TYPE
target_ratio = ASPECT_RATIOS.get(aspect_ratio, None)
# Resolve padding: legacy padding (px) overrides auto_padding_ratio if > 0
@@ -676,13 +710,13 @@ class FaceDetectionNodeV1:
"default": 35, "min": 0, "max": 100,
"tooltip": "Padding as % of detected face size",
}),
"aspect_ratio": ([["auto", "1:1", "9:16", "16:9", "4:3"]], {
"aspect_ratio": (["auto", "1:1", "9:16", "16:9", "4:3"], {
"default": "auto",
}),
"output_mode": ([["largest_face", "all_faces"]], {
"output_mode": (["largest_face", "all_faces"], {
"default": "largest_face",
}),
"classifier_type": ([["default", "alternative"]], {
"classifier_type": (["default", "alternative"], {
"default": "default",
}),
},
@@ -723,16 +757,40 @@ class FaceDetectionNodeV1:
DESCRIPTION = "Face Detection v2 — Auto-Padding, Temporal Smoothing, Aspect Ratios, Batch Processing"
@classmethod
def VALIDATE_INPUTS(cls, face_output_format=None, padding=None,
temporal_smoothing=None, output_height=None,
instance_id=None, **kwargs):
"""Validate optional inputs — gracefully handle type mismatches.
def VALIDATE_INPUTS(cls, aspect_ratio=None, output_mode=None,
classifier_type=None, face_output_format=None,
padding=None, temporal_smoothing=None,
output_height=None, instance_id=None, **kwargs):
"""Validate all combo and optional inputs — gracefully handle type mismatches.
When ComfyUI loads an old workflow, widgets_values are mapped positionally.
This can cause string values (e.g. 'default' from classifier_type) to land
on INT-typed inputs. We coerce or fall back to defaults here so the
framework validation doesn't crash.
This can cause string values (e.g. 'auto' from aspect_ratio) to land on
output_mode, or 'largest_face' to land on face_output_format. We validate
and replace invalid values with defaults so the framework doesn't crash.
By including required combo fields (aspect_ratio, output_mode,
classifier_type) in this method signature, ComfyUI delegates their
validation to us instead of doing strict framework-level checking.
"""
# Validate required combo fields (may receive wrong values from
# positional widget mapping in legacy workflows)
for name, val, valid_set, default in [
("aspect_ratio", aspect_ratio,
_VALID_ASPECT_RATIOS, _DEFAULT_ASPECT_RATIO),
("output_mode", output_mode,
_VALID_OUTPUT_MODES, _DEFAULT_OUTPUT_MODE),
("classifier_type", classifier_type,
_VALID_CLASSIFIER_TYPES, _DEFAULT_CLASSIFIER_TYPE),
("face_output_format", face_output_format,
_VALID_FACE_OUTPUT_FORMATS, _DEFAULT_FACE_OUTPUT_FORMAT),
]:
if val is not None and val not in valid_set:
logger.warning(
"FaceDetectionNode: invalid %s='%s', "
"using '%s' instead. Valid: %s",
name, val, default, list(valid_set),
)
# Coerce INT inputs that may arrive as strings from positional widget mapping
# Note: instance_id is now STRING — no longer needs INT coercion
for name, val in [
@@ -743,14 +801,6 @@ class FaceDetectionNodeV1:
if val is not None and not isinstance(val, int):
_coerce_int(val, name) # logs warning on failure, returns default
if face_output_format is not None:
if face_output_format not in _VALID_FACE_OUTPUT_FORMATS:
logger.warning(
"FaceDetectionNode: invalid face_output_format='%s', "
"using '%s' instead. Valid: %s",
face_output_format, _DEFAULT_FACE_OUTPUT_FORMAT,
list(_VALID_FACE_OUTPUT_FORMATS),
)
return True
@classmethod
@@ -784,6 +834,26 @@ class FaceDetectionNodeV1:
output_height = _coerce_int(output_height, "output_height", 512)
instance_id = str(instance_id) if instance_id is not None else "0"
padding = _coerce_int(padding, "padding", 0)
# Defensive: sanitize combo fields that may have wrong values from
# legacy workflows with misaligned positional widgets_values
if aspect_ratio not in _VALID_ASPECT_RATIOS:
logger.warning("FaceDetectionNode: aspect_ratio='%s' invalid, using '%s'",
aspect_ratio, _DEFAULT_ASPECT_RATIO)
aspect_ratio = _DEFAULT_ASPECT_RATIO
if output_mode not in _VALID_OUTPUT_MODES:
logger.warning("FaceDetectionNode: output_mode='%s' invalid, using '%s'",
output_mode, _DEFAULT_OUTPUT_MODE)
output_mode = _DEFAULT_OUTPUT_MODE
if classifier_type not in _VALID_CLASSIFIER_TYPES:
logger.warning("FaceDetectionNode: classifier_type='%s' invalid, using '%s'",
classifier_type, _DEFAULT_CLASSIFIER_TYPE)
classifier_type = _DEFAULT_CLASSIFIER_TYPE
if face_output_format not in _VALID_FACE_OUTPUT_FORMATS:
logger.warning("FaceDetectionNode: face_output_format='%s' invalid, using '%s'",
face_output_format, _DEFAULT_FACE_OUTPUT_FORMAT)
face_output_format = _DEFAULT_FACE_OUTPUT_FORMAT
cascade = CascadeCache.get(classifier_type)
if cascade is None:
logger.error("No cascade available")
@@ -800,14 +870,6 @@ class FaceDetectionNodeV1:
else:
pad_ratio = auto_padding_ratio / 100.0
# Resolve face_output_format with fallback
if face_output_format not in _VALID_FACE_OUTPUT_FORMATS:
logger.warning(
"FaceDetectionNode: invalid face_output_format='%s', using '%s'",
face_output_format, _DEFAULT_FACE_OUTPUT_FORMAT,
)
face_output_format = _DEFAULT_FACE_OUTPUT_FORMAT
detect_all = (output_mode == "all_faces")
temporal = self._get_temporal(instance_id, temporal_smoothing)
+1 -1
View File
@@ -1,7 +1,7 @@
[project]
name = "ComfyUI_FaceDetectionNode"
description = "H100-optimized face detection with Auto-Padding, Temporal Smoothing, Aspect Ratio Presets, and full batch processing for LTX-Video avatar pipelines. Backward compatible with v1.x workflows."
version = "2.1.2"
version = "2.1.3"
license = {text = "Apache-2.0"}
dependencies = ["opencv-python>=4.5.0", "torch>=2.0.0", "numpy>=1.21.0"]