refactor: process isolation support for node replacement API (#12298)

* refactor: process isolation support for node replacement API

- Move REGISTERED_NODE_REPLACEMENTS global to NodeReplaceManager instance state
- Add NodeReplacement class to ComfyAPI_latest with async register() method
- Deprecate module-level register_node_replacement() function
- Call register_replacements() from comfy_entrypoint()

This enables pyisolate compatibility where extensions run in separate
processes and communicate via RPC. The async API allows registration
calls to cross process boundaries.

Refs: TDD-002
Amp-Thread-ID: https://ampcode.com/threads/T-019c2b33-ac55-76a9-9c6b-0246a8625f21

* fix: remove whitespace and deprecation cruft

Amp-Thread-ID: https://ampcode.com/threads/T-019c2be8-0b34-747e-b1f7-20a1a1e6c9df
This commit is contained in:
Christian Byrne
2026-02-05 12:21:03 -08:00
committed by GitHub
parent d5b3da823d
commit a2d4c0f98b
4 changed files with 65 additions and 55 deletions

View File

@@ -656,21 +656,22 @@ class BatchImagesMasksLatentsNode(io.ComfyNode):
return io.NodeOutput(batched)
from comfy_api.latest import node_replace
from comfy_api.latest import ComfyAPI, node_replace
def register_replacements():
register_replacements_longeredge()
register_replacements_batchimages()
register_replacements_upscaleimage()
register_replacements_controlnet()
register_replacements_load3d()
register_replacements_preview3d()
register_replacements_svdimg2vid()
register_replacements_conditioningavg()
async def register_replacements():
"""Register all built-in node replacements using the async API."""
await register_replacements_longeredge()
await register_replacements_batchimages()
await register_replacements_upscaleimage()
await register_replacements_controlnet()
await register_replacements_load3d()
await register_replacements_preview3d()
await register_replacements_svdimg2vid()
await register_replacements_conditioningavg()
def register_replacements_longeredge():
async def register_replacements_longeredge():
# No dynamic inputs here
node_replace.register_node_replacement(node_replace.NodeReplace(
await ComfyAPI.node_replacement.register(node_replace.NodeReplace(
new_node_id="ImageScaleToMaxDimension",
old_node_id="ResizeImagesByLongerEdge",
old_widget_ids=["longer_edge"],
@@ -683,9 +684,9 @@ def register_replacements_longeredge():
output_mapping=[node_replace.OutputMap(new_idx=0, old_idx=0)],
))
def register_replacements_batchimages():
async def register_replacements_batchimages():
# BatchImages node uses Autogrow
node_replace.register_node_replacement(node_replace.NodeReplace(
await ComfyAPI.node_replacement.register(node_replace.NodeReplace(
new_node_id="BatchImagesNode",
old_node_id="ImageBatch",
input_mapping=[
@@ -694,9 +695,9 @@ def register_replacements_batchimages():
],
))
def register_replacements_upscaleimage():
async def register_replacements_upscaleimage():
# ResizeImageMaskNode uses DynamicCombo
node_replace.register_node_replacement(node_replace.NodeReplace(
await ComfyAPI.node_replacement.register(node_replace.NodeReplace(
new_node_id="ResizeImageMaskNode",
old_node_id="ImageScaleBy",
old_widget_ids=["upscale_method", "scale_by"],
@@ -708,9 +709,9 @@ def register_replacements_upscaleimage():
],
))
def register_replacements_controlnet():
async def register_replacements_controlnet():
# T2IAdapterLoader → ControlNetLoader
node_replace.register_node_replacement(node_replace.NodeReplace(
await ComfyAPI.node_replacement.register(node_replace.NodeReplace(
new_node_id="ControlNetLoader",
old_node_id="T2IAdapterLoader",
input_mapping=[
@@ -718,30 +719,30 @@ def register_replacements_controlnet():
],
))
def register_replacements_load3d():
async def register_replacements_load3d():
# Load3DAnimation merged into Load3D
node_replace.register_node_replacement(node_replace.NodeReplace(
await ComfyAPI.node_replacement.register(node_replace.NodeReplace(
new_node_id="Load3D",
old_node_id="Load3DAnimation",
))
def register_replacements_preview3d():
async def register_replacements_preview3d():
# Preview3DAnimation merged into Preview3D
node_replace.register_node_replacement(node_replace.NodeReplace(
await ComfyAPI.node_replacement.register(node_replace.NodeReplace(
new_node_id="Preview3D",
old_node_id="Preview3DAnimation",
))
def register_replacements_svdimg2vid():
async def register_replacements_svdimg2vid():
# Typo fix: SDV → SVD
node_replace.register_node_replacement(node_replace.NodeReplace(
await ComfyAPI.node_replacement.register(node_replace.NodeReplace(
new_node_id="SVD_img2vid_Conditioning",
old_node_id="SDV_img2vid_Conditioning",
))
def register_replacements_conditioningavg():
async def register_replacements_conditioningavg():
# Typo fix: trailing space in node name
node_replace.register_node_replacement(node_replace.NodeReplace(
await ComfyAPI.node_replacement.register(node_replace.NodeReplace(
new_node_id="ConditioningAverage",
old_node_id="ConditioningAverage ",
))
@@ -763,4 +764,5 @@ class PostProcessingExtension(ComfyExtension):
]
async def comfy_entrypoint() -> PostProcessingExtension:
await register_replacements()
return PostProcessingExtension()