33import weakref
44import os
55import copy
6+ import json
7+ from datetime import datetime
68from pathlib import Path
79import folder_paths
810import comfy .model_management as mm
2123)
2224
2325WEB_DIRECTORY = "./web"
24- MGPU_MM_LOG = False
26+ MGPU_MM_LOG = True
2527DEBUG_LOG = False
2628
2729logger = logging .getLogger ("MultiGPU" )
2830logger .propagate = False
2931
32+ FOCUS_LOG_LEVEL = logging .INFO + 5
33+ logging .addLevelName (FOCUS_LOG_LEVEL , "FOCUS" )
34+
35+ if not hasattr (logging .Logger , "focus" ):
36+ def focus (self , message , * args , ** kwargs ):
37+ if self .isEnabledFor (FOCUS_LOG_LEVEL ):
38+ self ._log (FOCUS_LOG_LEVEL , message , args , ** kwargs )
39+
40+ logging .Logger .focus = focus # type: ignore[attr-defined]
41+
3042if not logger .handlers :
3143 log_level = logging .DEBUG if DEBUG_LOG else logging .INFO
3244 handler = logging .StreamHandler ()
3547 logger .addHandler (handler )
3648 logger .setLevel (log_level )
3749
50+ json_log_path = os .environ .get ("MGPU_JSON_LOG_PATH" )
51+ json_static_fields = {}
52+ if json_log_path :
53+ try :
54+ json_static_fields = json .loads (os .environ .get ("MGPU_JSON_STATIC_FIELDS" , "{}" ))
55+ except json .JSONDecodeError :
56+ json_static_fields = {}
57+
58+ level_aliases = {
59+ "CRITICAL" : logging .CRITICAL ,
60+ "ERROR" : logging .ERROR ,
61+ "WARNING" : logging .WARNING ,
62+ "FOCUS" : FOCUS_LOG_LEVEL ,
63+ "INFO" : logging .INFO ,
64+ "DEBUG" : logging .DEBUG ,
65+ }
66+
67+ json_min_level = FOCUS_LOG_LEVEL
68+ configured_min_level = os .environ .get ("MGPU_JSON_MIN_LEVEL" )
69+ if configured_min_level :
70+ value = configured_min_level .strip ()
71+ upper_value = value .upper ()
72+ if upper_value in level_aliases :
73+ json_min_level = level_aliases [upper_value ]
74+ else :
75+ try :
76+ json_min_level = int (value )
77+ except ValueError :
78+ json_min_level = FOCUS_LOG_LEVEL
79+
80+ class JsonLineFileHandler (logging .Handler ):
81+ def __init__ (self , path , static_fields , min_level , overwrite ):
82+ super ().__init__ ()
83+ self .path = Path (path )
84+ self .path .parent .mkdir (parents = True , exist_ok = True )
85+ self .static_fields = static_fields
86+ self .setLevel (min_level )
87+ if overwrite :
88+ try :
89+ with self .path .open ("w" , encoding = "utf-8" ) as handle :
90+ handle .write ("" )
91+ except OSError :
92+ pass
93+
94+ def emit (self , record ):
95+ message = record .getMessage ()
96+ category = None
97+ if message .startswith ("[" ) and "]" in message :
98+ bracket_split = message .split ("]" , 1 )
99+ category = bracket_split [0 ].strip ("[]" )
100+ payload = {
101+ "timestamp" : datetime .utcnow ().isoformat () + "Z" ,
102+ "level" : record .levelname ,
103+ "name" : record .name ,
104+ "message" : message ,
105+ }
106+ if category :
107+ payload ["event_category" ] = category
108+ if hasattr (record , "mgpu_context" ) and isinstance (record .mgpu_context , dict ):
109+ payload .update (record .mgpu_context )
110+ workflow_id = os .environ .get ("MGPU_JSON_WORKFLOW" )
111+ prompt_id = os .environ .get ("MGPU_JSON_PROMPT" )
112+ if workflow_id :
113+ payload .setdefault ("workflow_id" , workflow_id )
114+ if prompt_id :
115+ payload .setdefault ("prompt_id" , prompt_id )
116+ if self .static_fields :
117+ payload .update (self .static_fields )
118+ try :
119+ with self .path .open ("a" , encoding = "utf-8" ) as handle :
120+ handle .write (json .dumps (payload , ensure_ascii = True ) + "\n " )
121+ except OSError :
122+ # Fail silently for JSON logging so primary logging continues.
123+ pass
124+
125+ overwrite_value = os .environ .get ("MGPU_JSON_OVERWRITE" , "true" ).strip ().lower ()
126+ overwrite_enabled = overwrite_value not in {"0" , "false" , "no" }
127+
128+ logger .addHandler (JsonLineFileHandler (json_log_path , json_static_fields , json_min_level , overwrite_enabled ))
129+
38130def mgpu_mm_log_method (self , msg ):
39131 """Add MultiGPU model management logging method to logger instance."""
40132 if MGPU_MM_LOG :
41- self .info (f"[MultiGPU Model Management] { msg } " )
133+ self .focus (
134+ f"[MultiGPU Model Management] { msg } " ,
135+ extra = {"mgpu_context" : {"component" : "model_management" }},
136+ )
42137logger .mgpu_mm_log = mgpu_mm_log_method .__get__ (logger , type (logger ))
43138
44139def check_module_exists (module_path ):
@@ -95,8 +190,6 @@ def text_encoder_device_patched():
95190mm .text_encoder_device = text_encoder_device_patched
96191
97192from .nodes import (
98- DeviceSelectorMultiGPU ,
99- HunyuanVideoEmbeddingsAdapter ,
100193 UnetLoaderGGUF ,
101194 UnetLoaderGGUFAdvanced ,
102195 CLIPLoaderGGUF ,
@@ -114,21 +207,30 @@ def text_encoder_device_patched():
114207 PulidModelLoader ,
115208 PulidInsightFaceLoader ,
116209 PulidEvaClipLoader ,
117- HyVideoModelLoader ,
118- HyVideoVAELoader ,
119- DownloadAndLoadHyVideoTextEncoder ,
120210 UNetLoaderLP ,
121211)
122212
123213from .wanvideo import (
124- WanVideoModelLoader ,
125- WanVideoModelLoader_2 ,
126- WanVideoVAELoader ,
127214 LoadWanVideoT5TextEncoder ,
128- LoadWanVideoClipTextEncoder ,
129215 WanVideoTextEncode ,
216+ WanVideoTextEncodeCached ,
217+ WanVideoTextEncodeSingle ,
218+ WanVideoVAELoader ,
219+ WanVideoTinyVAELoader ,
130220 WanVideoBlockSwap ,
131- WanVideoSampler
221+ WanVideoImageToVideoEncode ,
222+ WanVideoDecode ,
223+ WanVideoModelLoader ,
224+ WanVideoSampler ,
225+ WanVideoVACEEncode ,
226+ WanVideoEncode ,
227+ LoadWanVideoClipTextEncoder ,
228+ WanVideoClipVisionEncode ,
229+ WanVideoControlnetLoader ,
230+ FantasyTalkingModelLoader ,
231+ Wav2VecModelLoader ,
232+ WanVideoUni3C_ControlnetLoader ,
233+ DownloadAndLoadWav2VecModel ,
132234)
133235
134236from .wrappers import (
@@ -158,8 +260,6 @@ def text_encoder_device_patched():
158260)
159261
160262NODE_CLASS_MAPPINGS = {
161- "DeviceSelectorMultiGPU" : DeviceSelectorMultiGPU ,
162- "HunyuanVideoEmbeddingsAdapter" : HunyuanVideoEmbeddingsAdapter ,
163263 "CheckpointLoaderAdvancedMultiGPU" : CheckpointLoaderAdvancedMultiGPU ,
164264 "CheckpointLoaderAdvancedDisTorch2MultiGPU" : CheckpointLoaderAdvancedDisTorch2MultiGPU ,
165265 "UNetLoaderLP" : UNetLoaderLP ,
@@ -266,27 +366,32 @@ def register_and_count(module_names, node_map):
266366}
267367register_and_count (["PuLID_ComfyUI" , "pulid_comfyui" ], pulid_nodes )
268368
269- hunyuan_nodes = {
270- "HyVideoModelLoaderMultiGPU" : override_class (HyVideoModelLoader ),
271- "HyVideoVAELoaderMultiGPU" : override_class (HyVideoVAELoader ),
272- "DownloadAndLoadHyVideoTextEncoderMultiGPU" : override_class (DownloadAndLoadHyVideoTextEncoder )
273- }
274- register_and_count (["ComfyUI-HunyuanVideoWrapper" , "comfyui-hunyuanvideowrapper" ], hunyuan_nodes )
275-
276369wanvideo_nodes = {
277- "WanVideoModelLoaderMultiGPU" : WanVideoModelLoader ,
278- "WanVideoModelLoaderMultiGPU_2" : WanVideoModelLoader_2 ,
279- "WanVideoVAELoaderMultiGPU" : WanVideoVAELoader ,
280370 "LoadWanVideoT5TextEncoderMultiGPU" : LoadWanVideoT5TextEncoder ,
281- "LoadWanVideoClipTextEncoderMultiGPU" : LoadWanVideoClipTextEncoder ,
282371 "WanVideoTextEncodeMultiGPU" : WanVideoTextEncode ,
372+ "WanVideoTextEncodeCachedMultiGPU" : WanVideoTextEncodeCached ,
373+ "WanVideoTextEncodeSingleMultiGPU" : WanVideoTextEncodeSingle ,
374+ "WanVideoVAELoaderMultiGPU" : WanVideoVAELoader ,
375+ "WanVideoTinyVAELoaderMultiGPU" : WanVideoTinyVAELoader ,
283376 "WanVideoBlockSwapMultiGPU" : WanVideoBlockSwap ,
284- "WanVideoSamplerMultiGPU" : WanVideoSampler
377+ "WanVideoImageToVideoEncodeMultiGPU" : WanVideoImageToVideoEncode ,
378+ "WanVideoDecodeMultiGPU" : WanVideoDecode ,
379+ "WanVideoModelLoaderMultiGPU" : WanVideoModelLoader ,
380+ "WanVideoSamplerMultiGPU" : WanVideoSampler ,
381+ "WanVideoVACEEncodeMultiGPU" : WanVideoVACEEncode ,
382+ "WanVideoEncodeMultiGPU" : WanVideoEncode ,
383+ "LoadWanVideoClipTextEncoderMultiGPU" : LoadWanVideoClipTextEncoder ,
384+ "WanVideoClipVisionEncodeMultiGPU" : WanVideoClipVisionEncode ,
385+ "WanVideoControlnetLoaderMultiGPU" : WanVideoControlnetLoader ,
386+ "FantasyTalkingModelLoaderMultiGPU" : FantasyTalkingModelLoader ,
387+ "Wav2VecModelLoaderMultiGPU" : Wav2VecModelLoader ,
388+ "WanVideoUni3C_ControlnetLoaderMultiGPU" : WanVideoUni3C_ControlnetLoader ,
389+ "DownloadAndLoadWav2VecModelMultiGPU" : DownloadAndLoadWav2VecModel ,
285390}
286391register_and_count (["ComfyUI-WanVideoWrapper" , "comfyui-wanvideowrapper" ], wanvideo_nodes )
287392
288393for item in registration_data :
289394 logger .info (fmt_reg .format (item ['name' ], item ['found' ], str (item ['count' ])))
290395logger .info (dash_line )
291396
292- logger .info (f"[MultiGPU] Registration complete. Final mappings: { ', ' .join (NODE_CLASS_MAPPINGS .keys ())} " )
397+ logger .info (f"[MultiGPU] Registration complete. Final mappings: { ', ' .join (NODE_CLASS_MAPPINGS .keys ())} " )
0 commit comments