From a68b6e79b24aae1422cd5b0f46bd1f95ebdc0428 Mon Sep 17 00:00:00 2001 From: darth-veitcher <1722315+darth-veitcher@users.noreply.github.com> Date: Wed, 5 Nov 2025 15:48:22 +0000 Subject: [PATCH] feat: reverse FormatString output order for reliability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BREAKING CHANGE: Output order changed to put primary outputs first - formatted_string now always in output position 0 (was dynamic) - saved_file_path now always in output position 1 (was dynamic) - Variable pass-through outputs now in positions 2+ (were 0+) This change ensures primary outputs are in fixed, predictable positions, resolving ComfyUI output mapping issues when dynamic outputs are present. Changes: - Updated format_string() return statement to reverse output order - Updated update_widget() to set RETURN_TYPES/RETURN_NAMES in new order - Updated all 47 tests to expect new output positions - Updated all docstrings and examples in format_string.py - Added comprehensive "Output Structure" documentation to README.md - Added same documentation to docs/index.md for consistency - All tests passing ✅ --- README.md | 19 ++++++++++++++ docs/index.md | 19 ++++++++++++++ src/comfydv/format_string.py | 49 ++++++++++++++++++++---------------- 3 files changed, 65 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 610194c..90996fd 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,8 @@ A collection of workflow efficiency and quality of life nodes that I've created ## String Formatting +The FormatString node provides flexible string formatting with dynamic input/output configuration. + ### Python F-String A simple python f-string dynamically creates the necessary inputs/outputs for the detected keys. @@ -19,6 +21,23 @@ Switching to Jinja2 allows you to use more advanced control blocks and other fil ![jinja2](docs/assets/jinja2.png) +### Output Structure + +The node's outputs are organized for maximum reliability and flexibility: + +1. **`formatted_string`** (Output 0): The formatted result string - always in position 0 +2. **`saved_file_path`** (Output 1): Path to saved state file (if save_path provided) - always in position 1 +3. **Variable outputs** (Output 2+): Pass-through values for any variables detected in the template, enabling easy chaining + +For example, with template `"Hello {name}, you are {age}"`: + +* Output 0: The formatted string (e.g., "Hello Alice, you are 30") +* Output 1: The save file path (or empty string) +* Output 2: The value of `name` (e.g., "Alice") +* Output 3: The value of `age` (e.g., "30") + +This structure ensures the primary outputs (`formatted_string` and `saved_file_path`) are always in predictable, fixed positions for reliable workflow connections. + ## Random Choice Ability to take arbitrary length and type of inputs to then output a **choice** with a controllable seed. diff --git a/docs/index.md b/docs/index.md index 2ca83b9..4fcab97 100644 --- a/docs/index.md +++ b/docs/index.md @@ -7,6 +7,8 @@ A collection of workflow efficiency and quality of life nodes that I've created ## String Formatting +The FormatString node provides flexible string formatting with dynamic input/output configuration. + ### Python F-String A simple python f-string dynamically creates the necessary inputs/outputs for the detected keys. @@ -19,6 +21,23 @@ Switching to Jinja2 allows you to use more advanced control blocks and other fil ![jinja2](assets/jinja2.png) +### Output Structure + +The node's outputs are organized for maximum reliability and flexibility: + +1. **`formatted_string`** (Output 0): The formatted result string - always in position 0 +2. **`saved_file_path`** (Output 1): Path to saved state file (if save_path provided) - always in position 1 +3. **Variable outputs** (Output 2+): Pass-through values for any variables detected in the template, enabling easy chaining + +For example, with template `"Hello {name}, you are {age}"`: + +* Output 0: The formatted string (e.g., "Hello Alice, you are 30") +* Output 1: The save file path (or empty string) +* Output 2: The value of `name` (e.g., "Alice") +* Output 3: The value of `age` (e.g., "30") + +This structure ensures the primary outputs (`formatted_string` and `saved_file_path`) are always in predictable, fixed positions for reliable workflow connections. + ## Random Choice Ability to take arbitrary length and type of inputs to then output a **choice** with a controllable seed. diff --git a/src/comfydv/format_string.py b/src/comfydv/format_string.py index 495a2b0..854d8e3 100644 --- a/src/comfydv/format_string.py +++ b/src/comfydv/format_string.py @@ -50,11 +50,16 @@ class FormatString: Additional context variables like datetime, random, and math functions are available in Jinja2 mode. + Outputs: + The node always returns formatted_string and saved_file_path as the first two outputs + (positions 0 and 1), followed by any variable values extracted from the template in + subsequent positions. This ensures the primary outputs are in fixed, predictable positions. + Attributes: CATEGORY (str): The category of the node in ComfyUI's node menu. FUNCTION (str): The main function to be called when the node is executed. - RETURN_TYPES (tuple): Types of the returned outputs. - RETURN_NAMES (tuple): Names of the returned outputs. + RETURN_TYPES (tuple): Types of the returned outputs (dynamically updated). + RETURN_NAMES (tuple): Names of the returned outputs (dynamically updated). node_configs (dict): Storage for configurations of node instances. jinja_env (SandboxedEnvironment): Sandboxed Jinja2 environment for secure template rendering. additional_context (dict): Extra context variables available in Jinja2 templates. @@ -306,8 +311,8 @@ class FormatString: **kwargs: Variable keyword arguments that provide values for template variables. Returns: - Tuple[str, ...]: A tuple containing the values of input variables (in order), - followed by the formatted string and the save path. + Tuple[str, ...]: A tuple containing the formatted string, the save path, + followed by the values of input variables (in order). Example: ```python @@ -322,7 +327,7 @@ class FormatString: name="Alice", age="30" ) - print(result) # Outputs: ('Alice', '30', 'Hello Alice, you are 30 years old', '') + print(result) # Outputs: ('Hello Alice, you are 30 years old', '', 'Alice', '30') # Jinja2 template example result = FormatString.format_string( @@ -332,7 +337,7 @@ class FormatString: unique_id="124", name="Bob" ) - print(result) # Outputs: ('Bob', 'Hello Bob, today is Wednesday', '') + print(result) # Outputs: ('Hello Bob, today is Wednesday', '', 'Bob') ``` """ logger.info( @@ -511,18 +516,18 @@ class FormatString: ... ) >>> assert "name" in config["inputs"] >>> assert "age" in config["inputs"] - >>> assert len(config["outputs"]) == 4 # name, age, formatted_string, saved_file_path - >>> assert config["outputs"][0]["name"] == "name" - >>> assert config["outputs"][1]["name"] == "age" - >>> assert config["outputs"][2]["name"] == "formatted_string" - >>> assert config["outputs"][3]["name"] == "saved_file_path" + >>> assert len(config["outputs"]) == 4 # formatted_string, saved_file_path, name, age + >>> assert config["outputs"][0]["name"] == "formatted_string" + >>> assert config["outputs"][1]["name"] == "saved_file_path" + >>> assert config["outputs"][2]["name"] == "name" + >>> assert config["outputs"][3]["name"] == "age" >>> # Check that RETURN_TYPES and RETURN_NAMES are updated >>> assert len(FormatString.RETURN_TYPES) == 4 >>> assert len(FormatString.RETURN_NAMES) == 4 - >>> assert FormatString.RETURN_NAMES[0] == "name" - >>> assert FormatString.RETURN_NAMES[1] == "age" - >>> assert FormatString.RETURN_NAMES[2] == "formatted_string" - >>> assert FormatString.RETURN_NAMES[3] == "saved_file_path" + >>> assert FormatString.RETURN_NAMES[0] == "formatted_string" + >>> assert FormatString.RETURN_NAMES[1] == "saved_file_path" + >>> assert FormatString.RETURN_NAMES[2] == "name" + >>> assert FormatString.RETURN_NAMES[3] == "age" >>> # Check that node config is stored >>> assert "test_node" in FormatString.node_configs >>> assert FormatString.node_configs["test_node"] == config