@@ -86,6 +86,78 @@ IPipWheelService pipWheelService
8686 /// </summary>
8787 private bool IsAmdRocm => GetRecommendedTorchVersion ( ) == TorchIndex . Rocm ;
8888
89+ /// <summary>
90+ /// Python wrapper script that patches logging to also print to stdout/stderr, so
91+ /// StabilityMatrix can capture the output. Wan2GP logs through Gradio UI notifications
92+ /// (gr.Info/Warning/Error) and callback-driven UI updates that never reach the console.
93+ /// This script:
94+ /// 1. Configures Python's logging module to output to stderr (captures library logging)
95+ /// 2. Prevents transformers from suppressing its own logging (wgp.py calls set_verbosity_error)
96+ /// 3. Monkey-patches gr.Info/Warning/Error to also print to stdout/stderr
97+ /// 4. Runs the target script (wgp.py) via runpy
98+ /// </summary>
99+ private const string GradioLogPatchScript = """
100+ # StabilityMatrix: Patch logging to print to console for capture.
101+ import sys
102+ import logging
103+
104+ def _apply_logging_patch():
105+ # Configure Python's root logger to output to stderr at INFO level.
106+ # Many libraries (torch, diffusers, transformers, etc.) use the logging
107+ # module but output may be suppressed without a handler configured.
108+ root = logging.getLogger()
109+ if not any(isinstance(h, logging.StreamHandler) for h in root.handlers):
110+ handler = logging.StreamHandler(sys.stderr)
111+ handler.setFormatter(logging.Formatter("[%(name)s] %(levelname)s: %(message)s"))
112+ root.addHandler(handler)
113+ if root.level > logging.INFO:
114+ root.setLevel(logging.INFO)
115+
116+ # Prevent transformers from suppressing its own logging.
117+ # wgp.py calls transformers.utils.logging.set_verbosity_error() which
118+ # silences all non-error messages. We neutralize those calls so model
119+ # loading and download messages remain visible.
120+ try:
121+ import transformers.utils.logging as tf_logging
122+ tf_logging.set_verbosity_error = lambda: None
123+ tf_logging.set_verbosity_warning = lambda: None
124+ tf_logging.set_verbosity(logging.INFO)
125+ except Exception:
126+ pass
127+
128+ # Monkey-patch Gradio's UI notification functions to also print to console.
129+ # These only fire for validation/error messages, not generation progress.
130+ try:
131+ import gradio as gr
132+ _orig_info = getattr(gr, 'Info', None)
133+ _orig_warning = getattr(gr, 'Warning', None)
134+ _orig_error = getattr(gr, 'Error', None)
135+ if _orig_info is not None:
136+ def patched_info(message, *args, **kwargs):
137+ print(f"[Gradio] {message}", flush=True)
138+ return _orig_info(message, *args, **kwargs)
139+ gr.Info = patched_info
140+ if _orig_warning is not None:
141+ def patched_warning(message, *args, **kwargs):
142+ print(f"[Gradio] WARNING: {message}", flush=True)
143+ return _orig_warning(message, *args, **kwargs)
144+ gr.Warning = patched_warning
145+ if _orig_error is not None:
146+ def patched_error(message, *args, **kwargs):
147+ print(f"[Gradio] ERROR: {message}", file=sys.stderr, flush=True)
148+ return _orig_error(message, *args, **kwargs)
149+ gr.Error = patched_error
150+ except Exception:
151+ pass
152+
153+ if __name__ == "__main__":
154+ _apply_logging_patch()
155+ target_script = sys.argv[1]
156+ sys.argv = sys.argv[1:]
157+ import runpy
158+ runpy.run_path(target_script, run_name="__main__")
159+ """ ;
160+
89161 public override List < LaunchOptionDefinition > LaunchOptions =>
90162 [
91163 new ( )
@@ -386,13 +458,22 @@ await SetupVenv(installLocation, pythonVersion: PyVersion.Parse(installedPackage
386458 return env ;
387459 } ) ;
388460
461+ // Write the Gradio logging patch wrapper script so gr.Info/Warning/Error
462+ // messages are also printed to stdout/stderr for console capture
463+ var patchScriptPath = Path . Combine ( installLocation , "_sm_gradio_log_patch.py" ) ;
464+ await File . WriteAllTextAsync ( patchScriptPath , GradioLogPatchScript , cancellationToken )
465+ . ConfigureAwait ( false ) ;
466+
467+ var targetScript = Path . Combine ( installLocation , options . Command ?? LaunchCommand ) ;
468+
389469 // Notify user that the package is starting (loading can take a while)
390470 onConsoleOutput ? . Invoke (
391471 new ProcessOutput { Text = "Launching Wan2GP, please wait while the UI initializes...\n " }
392472 ) ;
393473
474+ // Launch via the patch wrapper, which monkey-patches Gradio then runs wgp.py
394475 VenvRunner . RunDetached (
395- [ Path . Combine ( installLocation , options . Command ?? LaunchCommand ) , .. options . Arguments ] ,
476+ [ patchScriptPath , targetScript , .. options . Arguments ] ,
396477 HandleConsoleOutput ,
397478 OnExit
398479 ) ;
0 commit comments