107 lines
3.9 KiB
Python
107 lines
3.9 KiB
Python
# comfy-nodes/string_utils.py
|
||
import logging
|
||
from typing import Any, Dict, List, Tuple
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
class JoinStringsMulti:
|
||
"""
|
||
A node to join multiple strings with a specified delimiter.
|
||
Can return either a single concatenated string or a list of strings.
|
||
The number of string inputs is dynamically adjustable in the UI.
|
||
"""
|
||
|
||
_last_input_count: int = 2 # Tracks the last known input count for dynamic UI generation
|
||
|
||
# ------------------------------------------------------------------
|
||
# ComfyUI metadata
|
||
# ------------------------------------------------------------------
|
||
RETURN_TYPES = ("STRING",)
|
||
RETURN_NAMES = ("string",)
|
||
FUNCTION = "join_strings"
|
||
CATEGORY = "llm_toolkit/utils/text"
|
||
OUTPUT_NODE = False # Utility node, not an output node
|
||
|
||
# ------------------------------------------------------------------
|
||
@classmethod
|
||
def INPUT_TYPES(cls) -> Dict[str, Any]:
|
||
"""Defines the input types for the node (dynamic based on `input_count`)."""
|
||
# Base fields that are always present
|
||
inputs: Dict[str, Any] = {
|
||
"required": {
|
||
"input_count": ("INT", {"default": 2, "min": 2, "max": 100, "step": 1}),
|
||
"delimiter": ("STRING", {"default": " ", "multiline": False}),
|
||
"return_list": ("BOOLEAN", {"default": False}),
|
||
}
|
||
}
|
||
|
||
# Add string_i fields (at least two by default, or `_last_input_count` if bigger)
|
||
max_fields = max(2, getattr(cls, "_last_input_count", 2))
|
||
for i in range(1, max_fields + 1):
|
||
# Force first two inputs; others optional
|
||
inputs["required"][f"string_{i}"] = (
|
||
"STRING",
|
||
{
|
||
"default": "",
|
||
"forceInput": i <= 2, # First two must be wired, rest optional
|
||
"multiline": False,
|
||
},
|
||
)
|
||
|
||
return inputs
|
||
|
||
# ------------------------------------------------------------------
|
||
# Dynamic update hook – triggers the "Update" button in ComfyUI
|
||
# ------------------------------------------------------------------
|
||
@classmethod
|
||
def IS_CHANGED(cls, input_count: int = 2, **_ignored):
|
||
"""Return a value that changes when `input_count` changes to trigger UI update."""
|
||
try:
|
||
input_count = int(input_count)
|
||
except Exception:
|
||
input_count = 2
|
||
|
||
input_count = max(2, min(100, input_count))
|
||
cls._last_input_count = input_count
|
||
return input_count
|
||
|
||
# ------------------------------------------------------------------
|
||
# Main execution
|
||
# ------------------------------------------------------------------
|
||
def join_strings(self, **kwargs) -> Tuple[List[str] | str]:
|
||
# Pull settings
|
||
input_count = int(kwargs.get("input_count", 2))
|
||
delimiter = kwargs.get("delimiter", " ")
|
||
return_list = bool(kwargs.get("return_list", False))
|
||
|
||
logger.debug(
|
||
"JoinStringsMulti: Joining %s strings with delimiter '%s'.", input_count, delimiter
|
||
)
|
||
|
||
# Collect strings based on the current input_count
|
||
collected: List[str] = []
|
||
for i in range(1, input_count + 1):
|
||
key = f"string_{i}"
|
||
if key not in kwargs:
|
||
continue # Skip if not provided (shouldn't happen after update)
|
||
val = kwargs[key]
|
||
if isinstance(val, list):
|
||
collected.extend([str(item) for item in val])
|
||
else:
|
||
collected.append(str(val))
|
||
|
||
if return_list:
|
||
return (collected,)
|
||
else:
|
||
return (delimiter.join(collected),)
|
||
|
||
|
||
# --- Node Mappings ---
|
||
NODE_CLASS_MAPPINGS = {
|
||
"JoinStringsMulti": JoinStringsMulti,
|
||
}
|
||
|
||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||
"JoinStringsMulti": "Join Strings Multi (LLMToolkit)",
|
||
} |