Skip to content

Add output socket to save nodes#13866

Open
alexisrolland wants to merge 11 commits into
masterfrom
alexis/add_output_save_nodes
Open

Add output socket to save nodes#13866
alexisrolland wants to merge 11 commits into
masterfrom
alexis/add_output_save_nodes

Conversation

@alexisrolland

@alexisrolland alexisrolland commented May 13, 2026

Copy link
Copy Markdown
Member

Add output socket to various save nodes to facilitate pipelining.

{2FB9573F-244C-4C1E-AA71-DA22D48EF9E4}

@coderabbitai

coderabbitai Bot commented May 13, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR updates save and preview nodes to declare and return their primary output data alongside UI payloads. Audio nodes (SaveAudio, SaveAudioMP3, SaveAudioOpus, PreviewAudio) now expose an "audio" output and return the input audio with UI payloads. Image nodes (SaveAnimatedWEBP, SaveAnimatedPNG, SaveImageAdvanced, SaveSVGNode) return images/svg alongside UI payloads and remove legacy aliases. SaveWEBM/SaveVideo declare explicit outputs and return their input media with UI payloads. Core nodes SaveLatent and SaveImage now declare return types/names and return the original data in result tuples alongside UI payloads.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding output sockets to save nodes to enable pipelining.
Description check ✅ Passed The description relates to the changeset by explaining the purpose of adding output sockets to save nodes and includes a visual workflow example demonstrating the feature in use.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@alexisrolland

Copy link
Copy Markdown
Member Author

Feedback from @comfyanonymous

  • It's the same as connecting the node to the previous output
  • This should be a frontend feature since the nodes are not doing any processing
  • We can add a way in the node schema to tell the frontend we want a passthrough output that is displayed on the node
  • It's way more efficient for the backend execution engine, less need for storing intermediates
  • This needs testing to make sure it doesn't mess with the workflow execution
  • Also the TODO: remove are to keep compatibility with some custom nodes

@alexisrolland alexisrolland deleted the alexis/add_output_save_nodes branch May 13, 2026 06:41
@alexisrolland alexisrolland changed the title Add output socket to save nodes (CORE-202) Add output socket to save nodes May 14, 2026
@alexisrolland alexisrolland restored the alexis/add_output_save_nodes branch June 2, 2026 01:28
@alexisrolland alexisrolland reopened this Jun 2, 2026
@alexisrolland

Copy link
Copy Markdown
Member Author

Bringing back this PR as per our discussion with @guill.

This would be a more straightforward way to do pass through.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
comfy_extras/nodes_images.py (2)

197-233: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Keep the legacy save_images / save_svg aliases for now.

This refactor removes the old entrypoint aliases from these save nodes, but the rest of comfy_extras/nodes_images.py still keeps the same compatibility pattern (crop = execute, repeat = execute, etc.). Dropping the aliases here makes these nodes a breaking interface change for older/custom integrations that still resolve save_images or save_svg.

Suggested compatibility shim
 class SaveAnimatedWEBP(IO.ComfyNode):
@@
     def execute(cls, images, fps, filename_prefix, lossless, quality, method, num_frames=0) -> IO.NodeOutput:
         return IO.NodeOutput(
             images,
             ui=UI.ImageSaveHelper.get_save_animated_webp_ui(
                 images=images,
                 filename_prefix=filename_prefix,
                 cls=cls,
                 fps=fps,
                 lossless=lossless,
                 quality=quality,
                 method=cls.COMPRESS_METHODS.get(method)
             )
         )
+
+    save_images = execute  # TODO: remove
@@
 class SaveAnimatedPNG(IO.ComfyNode):
@@
     def execute(cls, images, fps, compress_level, filename_prefix="ComfyUI") -> IO.NodeOutput:
         return IO.NodeOutput(
             images,
             ui=UI.ImageSaveHelper.get_save_animated_png_ui(
                 images=images,
                 filename_prefix=filename_prefix,
                 cls=cls,
                 fps=fps,
                 compress_level=compress_level,
             )
         )
+
+    save_images = execute  # TODO: remove
@@
 class SaveSVGNode(IO.ComfyNode):
@@
     def execute(cls, svg: IO.SVG.Type, filename_prefix="svg/ComfyUI") -> IO.NodeOutput:
@@
         return IO.NodeOutput(svg, ui={"images": results})
+
+    save_svg = execute  # TODO: remove

As per coding guidelines, comfy_extras/**: Community-contributed extra nodes. Focus on: ... No breaking changes to existing node interfaces.

Also applies to: 236-266, 497-566

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@comfy_extras/nodes_images.py` around lines 197 - 233, The SaveAnimatedWEBP
node removed legacy entrypoint aliases; to preserve backward compatibility add
legacy names that point to the existing execute implementation (e.g., append at
class scope: save_images = execute and save_svg = execute where appropriate) so
older integrations can call SaveAnimatedWEBP.save_images(...) or
SaveAnimatedWEBP.save_svg(...) and get the same behavior; locate the class
SaveAnimatedWEBP and add these alias assignments (and mirror the same pattern
for other affected classes/sections mentioned) rather than removing or renaming
the execute entrypoint.

520-566: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Rewind the SVG streams before passing svg downstream.

svg_bytes.read() leaves each buffer at EOF. That was harmless when this node returned only UI metadata, but with the new passthrough output the downstream node can now receive empty SVG streams unless it manually seeks back to 0.

Suggested fix
         for batch_number, svg_bytes in enumerate(svg.data):
             filename_with_batch_num = filename.replace("%batch_num%", str(batch_number))
             file = f"{filename_with_batch_num}_{counter:05}_.svg"

             # Read SVG content
             svg_bytes.seek(0)
             svg_content = svg_bytes.read().decode('utf-8')
+            svg_bytes.seek(0)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@comfy_extras/nodes_images.py` around lines 520 - 566, The SVG byte streams
are left at EOF after calling svg_bytes.read(), so downstream nodes receive
empty streams; in execute (method execute of the node) rewind each buffer in
svg.data back to position 0 after reading/modifying (e.g., call
svg_bytes.seek(0)) so that the returned svg (NodeOutput(svg, ...)) contains
seeked-to-start streams; ensure this is done for every svg_bytes in the loop (or
after the loop) so passthrough consumers get readable streams.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@comfy_extras/nodes_images.py`:
- Around line 197-233: The SaveAnimatedWEBP node removed legacy entrypoint
aliases; to preserve backward compatibility add legacy names that point to the
existing execute implementation (e.g., append at class scope: save_images =
execute and save_svg = execute where appropriate) so older integrations can call
SaveAnimatedWEBP.save_images(...) or SaveAnimatedWEBP.save_svg(...) and get the
same behavior; locate the class SaveAnimatedWEBP and add these alias assignments
(and mirror the same pattern for other affected classes/sections mentioned)
rather than removing or renaming the execute entrypoint.
- Around line 520-566: The SVG byte streams are left at EOF after calling
svg_bytes.read(), so downstream nodes receive empty streams; in execute (method
execute of the node) rewind each buffer in svg.data back to position 0 after
reading/modifying (e.g., call svg_bytes.seek(0)) so that the returned svg
(NodeOutput(svg, ...)) contains seeked-to-start streams; ensure this is done for
every svg_bytes in the loop (or after the loop) so passthrough consumers get
readable streams.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: da1535e8-33ff-4571-8dd3-e483a659411e

📥 Commits

Reviewing files that changed from the base of the PR and between cf66f99 and 1e724d6.

📒 Files selected for processing (2)
  • comfy_extras/nodes_images.py
  • comfy_extras/nodes_video.py

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant