1919workspace_manager = WorkspaceManager ()
2020
2121
22- def load_api_workflow ( file : str ) :
23- with open ( file , encoding = "utf-8" ) as f :
24- workflow = json . load ( f )
25- # Check for litegraph properties to ensure this isnt a UI workflow file
26- if "nodes" in workflow and "links" in workflow :
27- return None
22+ def is_ui_workflow ( workflow ) -> bool :
23+ return (
24+ isinstance ( workflow , dict )
25+ and isinstance ( workflow . get ( "nodes" ), list )
26+ and isinstance ( workflow . get ( "links" ), list )
27+ )
2828
29- # Try validating the first entry to ensure it has a node class property
30- node_id = next (iter (workflow ))
31- node = workflow [node_id ]
32- if "class_type" not in node :
33- return None
3429
35- return workflow
30+ def _validate_api_workflow (workflow ):
31+ """Return the workflow dict if it has the shape of API format, else None."""
32+ if not isinstance (workflow , dict ) or not workflow :
33+ return None
34+ node = workflow [next (iter (workflow ))]
35+ if not isinstance (node , dict ) or "class_type" not in node :
36+ return None
37+ return workflow
38+
39+
40+ class WorkflowConverterUnavailable (Exception ):
41+ """The running ComfyUI server doesn't expose /workflow/convert."""
42+
43+
44+ def convert_ui_workflow_via_server (workflow : dict , host : str , port : int , timeout : int ) -> dict :
45+ """POST a UI-format workflow to the server's /workflow/convert and return API-format JSON.
46+
47+ Raises WorkflowConverterUnavailable if the server doesn't expose the endpoint.
48+ Raises typer.Exit on other conversion failures.
49+ """
50+ url = f"http://{ host } :{ port } /workflow/convert"
51+ req = request .Request (url , json .dumps (workflow ).encode ("utf-8" ))
52+ req .add_header ("Content-Type" , "application/json" )
53+ try :
54+ resp = request .urlopen (req , timeout = timeout )
55+ except urllib .error .HTTPError as e :
56+ if e .code in (404 , 405 ):
57+ raise WorkflowConverterUnavailable () from e
58+ body = e .read ().decode ("utf-8" , errors = "replace" ).strip ()
59+ pprint (f"[bold red]Workflow conversion failed (HTTP { e .code } ): { body [:500 ]} [/bold red]" )
60+ raise typer .Exit (code = 1 ) from e
61+ except urllib .error .URLError as e :
62+ pprint (f"[bold red]Workflow conversion failed: { e .reason } [/bold red]" )
63+ raise typer .Exit (code = 1 ) from e
64+ try :
65+ converted = json .loads (resp .read ())
66+ except json .JSONDecodeError as e :
67+ pprint ("[bold red]Workflow conversion failed: server returned invalid JSON[/bold red]" )
68+ raise typer .Exit (code = 1 ) from e
69+ if not isinstance (converted , dict ) or not converted :
70+ pprint ("[bold red]Workflow conversion failed: expected a non-empty JSON object[/bold red]" )
71+ raise typer .Exit (code = 1 )
72+ first = converted [next (iter (converted ))]
73+ if not isinstance (first , dict ) or "class_type" not in first :
74+ pprint ("[bold red]Workflow conversion failed: returned data is not API workflow format[/bold red]" )
75+ raise typer .Exit (code = 1 )
76+ return converted
77+
78+
79+ def _print_converter_unavailable_help () -> None :
80+ pprint (
81+ "[bold red]This ComfyUI server doesn't expose a /workflow/convert endpoint[/bold red]\n "
82+ "[bold red]to convert it to API format.[/bold red]\n "
83+ "\n "
84+ "[yellow]Workarounds:[/yellow]\n "
85+ "[yellow] * Install a custom node that adds /workflow/convert on the server[/yellow]\n "
86+ "[yellow] * Or, in the ComfyUI frontend, use 'File > Export (API)' to save[/yellow]\n "
87+ "[yellow] your workflow as API format[/yellow]"
88+ )
3689
3790
3891def execute (workflow : str , host , port , wait = True , verbose = False , local_paths = False , timeout = 30 ):
@@ -44,16 +97,33 @@ def execute(workflow: str, host, port, wait=True, verbose=False, local_paths=Fal
4497 )
4598 raise typer .Exit (code = 1 )
4699
47- workflow = load_api_workflow (workflow )
48-
49- if not workflow :
50- pprint ("[bold red]Specified workflow does not appear to be an API workflow json file[/bold red]" )
51- raise typer .Exit (code = 1 )
52-
53100 if not check_comfy_server_running (port , host ):
54101 pprint (f"[bold red]ComfyUI not running on specified address ({ host } :{ port } )[/bold red]" )
55102 raise typer .Exit (code = 1 )
56103
104+ try :
105+ with open (workflow_name , encoding = "utf-8" ) as f :
106+ raw_workflow = json .load (f )
107+ except OSError as e :
108+ pprint (f"[bold red]Unable to read workflow file: { e } [/bold red]" )
109+ raise typer .Exit (code = 1 ) from e
110+ except json .JSONDecodeError as e :
111+ pprint (f"[bold red]Specified workflow file is not valid JSON: { e } [/bold red]" )
112+ raise typer .Exit (code = 1 ) from e
113+
114+ if is_ui_workflow (raw_workflow ):
115+ pprint ("[yellow]Detected UI-format workflow, converting via server's /workflow/convert...[/yellow]" )
116+ try :
117+ workflow = convert_ui_workflow_via_server (raw_workflow , host , port , timeout )
118+ except WorkflowConverterUnavailable :
119+ _print_converter_unavailable_help ()
120+ raise typer .Exit (code = 1 )
121+ else :
122+ workflow = _validate_api_workflow (raw_workflow )
123+ if not workflow :
124+ pprint ("[bold red]Specified workflow does not appear to be an API workflow json file[/bold red]" )
125+ raise typer .Exit (code = 1 )
126+
57127 progress = None
58128 start = time .time ()
59129 if wait :
0 commit comments