Simplify accumulator variant collection
This commit is contained in:
@@ -120,9 +120,20 @@ you want to update each iteration. They are separate from the collector and grow
|
|||||||
dynamically in the UI as you connect them.
|
dynamically in the UI as you connect them.
|
||||||
|
|
||||||
`SxCP Accumulator` stores outputs across executions under a `store_key` or the
|
`SxCP Accumulator` stores outputs across executions under a `store_key` or the
|
||||||
node id. Put it after an image-producing step inside or after a loop, connect the
|
node id. Put it after an image-producing step inside or after a loop and connect
|
||||||
generated `image`, and connect `For Loop Start.index` to `entry_id` when you want
|
the generated `image`.
|
||||||
reruns to replace the same row instead of appending duplicates. Its outputs are:
|
|
||||||
|
For camera/pose tuning, leave `action=append_variant`. Every rerun is kept as a
|
||||||
|
new variant, so you can regenerate row 1 several times without managing ids. If
|
||||||
|
you connect `For Loop Start.index` to `entry_id`, variants are labelled by row
|
||||||
|
internally; they still append instead of replacing.
|
||||||
|
|
||||||
|
For deterministic loop resume/dedupe, set `action=replace_by_entry_id` and
|
||||||
|
connect `For Loop Start.index` to `entry_id`. Optional `entry_tag` lets multiple
|
||||||
|
branches share the same row index without overwriting each other, such as
|
||||||
|
`soft`, `hard`, or `upscale`.
|
||||||
|
|
||||||
|
Its outputs are:
|
||||||
|
|
||||||
- `collection`: all stored values, or images when no explicit `value` is wired.
|
- `collection`: all stored values, or images when no explicit `value` is wired.
|
||||||
- `image_batch`: all stored images as one ComfyUI image batch when they share
|
- `image_batch`: all stored images as one ComfyUI image batch when they share
|
||||||
|
|||||||
+32
-14
@@ -22,7 +22,7 @@ except Exception:
|
|||||||
MAX_LOOP_VALUES = 20
|
MAX_LOOP_VALUES = 20
|
||||||
MAX_CARRY_VALUES = MAX_LOOP_VALUES - 2
|
MAX_CARRY_VALUES = MAX_LOOP_VALUES - 2
|
||||||
COLLECTION_MODES = ["auto_batch", "list", "image_batch", "latent_batch", "string_lines"]
|
COLLECTION_MODES = ["auto_batch", "list", "image_batch", "latent_batch", "string_lines"]
|
||||||
ACCUMULATOR_ACTIONS = ["replace_by_entry_id", "append", "clear_then_append", "clear", "read"]
|
ACCUMULATOR_ACTIONS = ["append_variant", "replace_by_entry_id", "append", "clear_then_append", "clear", "read"]
|
||||||
ACCUMULATOR_IMAGE_BATCH_MODES = ["same_size_only", "resize_to_first"]
|
ACCUMULATOR_IMAGE_BATCH_MODES = ["same_size_only", "resize_to_first"]
|
||||||
ACCUMULATOR_IMAGE_GROUPS = 4
|
ACCUMULATOR_IMAGE_GROUPS = 4
|
||||||
|
|
||||||
@@ -418,7 +418,7 @@ class SxCPAccumulator:
|
|||||||
return {
|
return {
|
||||||
"required": {
|
"required": {
|
||||||
"store_key": ("STRING", {"default": "", "multiline": False}),
|
"store_key": ("STRING", {"default": "", "multiline": False}),
|
||||||
"action": (ACCUMULATOR_ACTIONS, {"default": "replace_by_entry_id"}),
|
"action": (ACCUMULATOR_ACTIONS, {"default": "append_variant"}),
|
||||||
"max_items": ("INT", {"default": 32, "min": 1, "max": 10000, "step": 1}),
|
"max_items": ("INT", {"default": 32, "min": 1, "max": 10000, "step": 1}),
|
||||||
"image_batch_mode": (ACCUMULATOR_IMAGE_BATCH_MODES, {"default": "same_size_only"}),
|
"image_batch_mode": (ACCUMULATOR_IMAGE_BATCH_MODES, {"default": "same_size_only"}),
|
||||||
"skip_empty": ("BOOLEAN", {"default": True}),
|
"skip_empty": ("BOOLEAN", {"default": True}),
|
||||||
@@ -427,6 +427,7 @@ class SxCPAccumulator:
|
|||||||
"image": ("IMAGE",),
|
"image": ("IMAGE",),
|
||||||
"value": (ANY_TYPE,),
|
"value": (ANY_TYPE,),
|
||||||
"entry_id": (ANY_TYPE,),
|
"entry_id": (ANY_TYPE,),
|
||||||
|
"entry_tag": ("STRING", {"default": "", "multiline": False}),
|
||||||
},
|
},
|
||||||
"hidden": {
|
"hidden": {
|
||||||
"unique_id": "UNIQUE_ID",
|
"unique_id": "UNIQUE_ID",
|
||||||
@@ -451,15 +452,16 @@ class SxCPAccumulator:
|
|||||||
key = str(store_key or "").strip()
|
key = str(store_key or "").strip()
|
||||||
return key or f"node:{unique_id}"
|
return key or f"node:{unique_id}"
|
||||||
|
|
||||||
def _entry_id(self, entry_id: Any, image_index: int, image_count: int) -> str:
|
def _entry_id(self, entry_id: Any, entry_tag: str, image_index: int, image_count: int) -> str:
|
||||||
if entry_id is None:
|
text = "" if entry_id is None else str(entry_id).strip()
|
||||||
return ""
|
tag = str(entry_tag or "").strip()
|
||||||
text = str(entry_id).strip()
|
if text and tag:
|
||||||
if not text:
|
text = f"{text}:{tag}"
|
||||||
return ""
|
elif tag:
|
||||||
|
text = tag
|
||||||
if image_count <= 1:
|
if image_count <= 1:
|
||||||
return text
|
return text
|
||||||
return f"{text}:{image_index + 1}"
|
return f"{text or 'image'}:{image_index + 1}"
|
||||||
|
|
||||||
def _value_for_image(self, value: Any, image_index: int, image_count: int) -> Any:
|
def _value_for_image(self, value: Any, image_index: int, image_count: int) -> Any:
|
||||||
if image_count <= 1:
|
if image_count <= 1:
|
||||||
@@ -468,25 +470,40 @@ class SxCPAccumulator:
|
|||||||
return value[image_index]
|
return value[image_index]
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def _entry_records(self, image: Any, value: Any, entry_id: Any, skip_empty: bool) -> list[dict[str, Any]]:
|
def _entry_records(self, image: Any, value: Any, entry_id: Any, entry_tag: str, skip_empty: bool) -> list[dict[str, Any]]:
|
||||||
images = _split_image_value(image)
|
images = _split_image_value(image)
|
||||||
if not images:
|
if not images:
|
||||||
if value is None and skip_empty:
|
if value is None and skip_empty:
|
||||||
return []
|
return []
|
||||||
return [{"id": self._entry_id(entry_id, 0, 1), "image": None, "value": value}]
|
return [{"id": self._entry_id(entry_id, entry_tag, 0, 1), "image": None, "value": value}]
|
||||||
image_count = len(images)
|
image_count = len(images)
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
"id": self._entry_id(entry_id, index, image_count),
|
"id": self._entry_id(entry_id, entry_tag, index, image_count),
|
||||||
"image": image_item,
|
"image": image_item,
|
||||||
"value": self._value_for_image(value, index, image_count),
|
"value": self._value_for_image(value, index, image_count),
|
||||||
}
|
}
|
||||||
for index, image_item in enumerate(images)
|
for index, image_item in enumerate(images)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def _variant_entry_id(self, store: list[dict[str, Any]], base_id: str) -> str:
|
||||||
|
base_id = base_id or "entry"
|
||||||
|
prefix = f"{base_id}#"
|
||||||
|
count = 0
|
||||||
|
for existing in store:
|
||||||
|
existing_id = str(existing.get("id") or "")
|
||||||
|
if existing_id == base_id or existing_id.startswith(prefix):
|
||||||
|
count += 1
|
||||||
|
return f"{base_id}#{count + 1}"
|
||||||
|
|
||||||
def _append_or_replace(self, store: list[dict[str, Any]], entries: list[dict[str, Any]], action: str) -> None:
|
def _append_or_replace(self, store: list[dict[str, Any]], entries: list[dict[str, Any]], action: str) -> None:
|
||||||
replace = action == "replace_by_entry_id"
|
replace = action == "replace_by_entry_id"
|
||||||
for entry in entries:
|
for entry in entries:
|
||||||
|
if action == "append_variant":
|
||||||
|
entry = dict(entry)
|
||||||
|
entry["id"] = self._variant_entry_id(store, str(entry.get("id") or ""))
|
||||||
|
store.append(entry)
|
||||||
|
continue
|
||||||
entry_id = entry.get("id") or ""
|
entry_id = entry.get("id") or ""
|
||||||
if replace and entry_id:
|
if replace and entry_id:
|
||||||
for index, existing in enumerate(store):
|
for index, existing in enumerate(store):
|
||||||
@@ -529,17 +546,18 @@ class SxCPAccumulator:
|
|||||||
image=None,
|
image=None,
|
||||||
value=None,
|
value=None,
|
||||||
entry_id=None,
|
entry_id=None,
|
||||||
|
entry_tag="",
|
||||||
unique_id=None,
|
unique_id=None,
|
||||||
):
|
):
|
||||||
key = self._store_key(store_key, unique_id)
|
key = self._store_key(store_key, unique_id)
|
||||||
action = action if action in ACCUMULATOR_ACTIONS else "replace_by_entry_id"
|
action = action if action in ACCUMULATOR_ACTIONS else "append_variant"
|
||||||
store = _ACCUMULATOR_STORES.setdefault(key, [])
|
store = _ACCUMULATOR_STORES.setdefault(key, [])
|
||||||
|
|
||||||
if action in ("clear", "clear_then_append"):
|
if action in ("clear", "clear_then_append"):
|
||||||
store.clear()
|
store.clear()
|
||||||
|
|
||||||
if action not in ("clear", "read"):
|
if action not in ("clear", "read"):
|
||||||
entries = self._entry_records(image, value, entry_id, bool(skip_empty))
|
entries = self._entry_records(image, value, entry_id, entry_tag, bool(skip_empty))
|
||||||
self._append_or_replace(store, entries, action)
|
self._append_or_replace(store, entries, action)
|
||||||
|
|
||||||
max_items = max(1, int(max_items))
|
max_items = max(1, int(max_items))
|
||||||
|
|||||||
Reference in New Issue
Block a user