mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2026-03-14 13:47:44 +08:00
Adds opt-in process isolation for custom nodes using pyisolate's bwrap sandbox and JSON-RPC bridge. Each isolated node pack runs in its own child process with zero-copy tensor transfer via shared memory. Core infrastructure: - CLI flag --use-process-isolation to enable isolation - Host/child startup fencing via PYISOLATE_CHILD env var - Manifest-driven node discovery and extension loading - JSON-RPC bridge between host and child processes - Shared memory forensics for leak detection Proxy layer: - ModelPatcher, CLIP, VAE, and ModelSampling proxies - Host service proxies (folder_paths, model_management, progress, etc.) - Proxy base with automatic method forwarding Execution integration: - Extension wrapper with V3 hidden param mapping - Runtime helpers for isolated node execution - Host policy for node isolation decisions - Fenced sampler device handling and model ejection parity Serializers for cross-process data transfer: - File3D (GLB), PLY (structured + gaussian), NPZ (streaming frames), VIDEO (VideoFromFile + VideoFromComponents) serializers - data_type flag in SerializerRegistry for type-aware dispatch - Isolated get_temp_directory() fence New core save nodes: - SavePLY and SaveNPZ with comfytype registrations (Ply, Npz) DynamicVRAM compatibility: - comfy-aimdo early init gated by isolation fence Tests: - Integration and policy tests for isolation lifecycle - Manifest loader, host policy, proxy, and adapter unit tests Depends on: pyisolate >= 0.9.2
65 lines
2.1 KiB
Python
65 lines
2.1 KiB
Python
# pylint: disable=cyclic-import,import-outside-toplevel
|
|
from __future__ import annotations
|
|
|
|
from typing import Optional, Any
|
|
import comfy.utils
|
|
from pyisolate import ProxiedSingleton
|
|
|
|
import os
|
|
|
|
|
|
class UtilsProxy(ProxiedSingleton):
|
|
"""
|
|
Proxy for comfy.utils.
|
|
Primarily handles the PROGRESS_BAR_HOOK to ensure progress updates
|
|
from isolated nodes reach the host.
|
|
"""
|
|
|
|
# _instance and __new__ removed to rely on SingletonMetaclass
|
|
_rpc: Optional[Any] = None
|
|
|
|
@classmethod
|
|
def set_rpc(cls, rpc: Any) -> None:
|
|
# Create caller using class name as ID (standard for Singletons)
|
|
cls._rpc = rpc.create_caller(cls, "UtilsProxy")
|
|
|
|
async def progress_bar_hook(
|
|
self,
|
|
value: int,
|
|
total: int,
|
|
preview: Optional[bytes] = None,
|
|
node_id: Optional[str] = None,
|
|
) -> Any:
|
|
"""
|
|
Host-side implementation: forwards the call to the real global hook.
|
|
Child-side: this method call is intercepted by RPC and sent to host.
|
|
"""
|
|
if os.environ.get("PYISOLATE_CHILD") == "1":
|
|
# Manual RPC dispatch for Child process
|
|
# Use class-level RPC storage (Static Injection)
|
|
if UtilsProxy._rpc:
|
|
return await UtilsProxy._rpc.progress_bar_hook(
|
|
value, total, preview, node_id
|
|
)
|
|
|
|
# Fallback channel: global child rpc
|
|
try:
|
|
from pyisolate._internal.rpc_protocol import get_child_rpc_instance
|
|
|
|
get_child_rpc_instance()
|
|
# If we have an RPC instance but no UtilsProxy._rpc, we *could* try to use it,
|
|
# but we need a caller. For now, just pass to avoid crashing.
|
|
pass
|
|
except (ImportError, LookupError):
|
|
pass
|
|
|
|
return None
|
|
|
|
# Host Execution
|
|
if comfy.utils.PROGRESS_BAR_HOOK is not None:
|
|
comfy.utils.PROGRESS_BAR_HOOK(value, total, preview, node_id)
|
|
|
|
def set_progress_bar_global_hook(self, hook: Any) -> None:
|
|
"""Forward hook registration (though usually not needed from child)."""
|
|
comfy.utils.set_progress_bar_global_hook(hook)
|