@@ -126,6 +126,292 @@ def flow_start(
126126 raise typer .Abort ()
127127
128128
129+ @app .command ("import" )
130+ def flow_import (
131+ yaml_path : str = typer .Argument (..., help = "Path to YAML workflow file" ),
132+ langflow_url : str = typer .Option (
133+ "http://localhost:7860" ,
134+ "--url" ,
135+ help = "Langflow server URL"
136+ ),
137+ dry_run : bool = typer .Option (
138+ False ,
139+ "--dry-run" ,
140+ help = "Preview JSON without uploading"
141+ ),
142+ open_browser : bool = typer .Option (
143+ False ,
144+ "--open" ,
145+ help = "Open imported flow in browser"
146+ ),
147+ output : str = typer .Option (
148+ None ,
149+ "--output" ,
150+ "-o" ,
151+ help = "Save JSON to file instead of uploading"
152+ ),
153+ ):
154+ """Import YAML workflow into Langflow.
155+
156+ Converts PraisonAI YAML workflow to Langflow JSON format and uploads
157+ to a running Langflow instance for visual editing.
158+
159+ Examples:
160+ praisonai flow import workflow.yaml
161+ praisonai flow import workflow.yaml --dry-run
162+ praisonai flow import workflow.yaml --output flow.json
163+ praisonai flow import workflow.yaml --url http://localhost:8080
164+ """
165+ from pathlib import Path
166+ from rich .console import Console
167+ from rich .json import JSON
168+
169+ console = Console ()
170+
171+ # Validate input file
172+ yaml_file = Path (yaml_path )
173+ if not yaml_file .exists ():
174+ console .print (f"[red]Error: File not found: { yaml_path } [/red]" )
175+ raise typer .Abort ()
176+
177+ try :
178+ from praisonai .flow .converter import yaml_to_langflow_json
179+
180+ console .print (f"[cyan]Converting { yaml_path } to Langflow format...[/cyan]" )
181+
182+ # Convert YAML to Langflow JSON
183+ langflow_json = yaml_to_langflow_json (str (yaml_file ))
184+
185+ # Dry run: just show the JSON
186+ if dry_run :
187+ console .print ("\n [bold green]✅ Conversion Preview[/bold green]" )
188+ console .print (JSON .from_data (langflow_json , indent = 2 ))
189+ return
190+
191+ # Save to file mode
192+ if output :
193+ output_path = Path (output )
194+ output_path .parent .mkdir (parents = True , exist_ok = True )
195+
196+ import json
197+ with open (output_path , 'w' ) as f :
198+ json .dump (langflow_json , f , indent = 2 )
199+
200+ console .print (f"[green]✅ Flow saved to { output_path } [/green]" )
201+ return
202+
203+ # Upload to Langflow
204+ from praisonai .flow .client import create_client
205+
206+ console .print (f"[cyan]Connecting to Langflow at { langflow_url } ...[/cyan]" )
207+ client = create_client (langflow_url )
208+
209+ # Check server health
210+ health = client .health_check ()
211+ if health ["status" ] != "healthy" :
212+ console .print (f"[red]Error: Langflow server not accessible at { langflow_url } [/red]" )
213+ console .print ("[yellow]Make sure Langflow is running: praisonai flow[/yellow]" )
214+ raise typer .Abort ()
215+
216+ # Upload flow
217+ console .print ("[cyan]Uploading flow to Langflow...[/cyan]" )
218+ response = client .upload_flow (langflow_json )
219+
220+ flow_id = response .get ("id" , response .get ("flow_id" , "" ))
221+ flow_name = langflow_json .get ("name" , "Imported Flow" )
222+
223+ console .print (f"[green]✅ Flow '{ flow_name } ' imported successfully![/green]" )
224+ console .print (f"[dim]Flow ID: { flow_id } [/dim]" )
225+
226+ # Generate flow URL
227+ if flow_id :
228+ flow_url = f"{ langflow_url } /flow/{ flow_id } "
229+ console .print (f"[blue]View: { flow_url } [/blue]" )
230+
231+ # Open browser if requested
232+ if open_browser :
233+ import webbrowser
234+ webbrowser .open (flow_url )
235+ console .print ("[dim]Opening in browser...[/dim]" )
236+
237+ except ImportError as e :
238+ console .print (f"[red]Error: { e } [/red]" )
239+ console .print ("[yellow]Install with: pip install 'praisonai[flow]'[/yellow]" )
240+ raise typer .Abort ()
241+ except Exception as e :
242+ console .print (f"[red]Error importing flow: { e } [/red]" )
243+ raise typer .Abort ()
244+
245+
246+ @app .command ("export" )
247+ def flow_export (
248+ flow_id : str = typer .Argument (..., help = "Flow ID to export" ),
249+ output : str = typer .Option (
250+ None ,
251+ "--output" ,
252+ "-o" ,
253+ help = "Output YAML file path (default: flow_id.yaml)"
254+ ),
255+ langflow_url : str = typer .Option (
256+ "http://localhost:7860" ,
257+ "--url" ,
258+ help = "Langflow server URL"
259+ ),
260+ format : str = typer .Option (
261+ "yaml" ,
262+ "--format" ,
263+ help = "Output format (yaml, json)" ,
264+ type = typer .Choice (["yaml" , "json" ])
265+ ),
266+ ):
267+ """Export Langflow flow to YAML format.
268+
269+ Downloads a flow from Langflow and converts it back to PraisonAI
270+ YAML format for use with the CLI.
271+
272+ Examples:
273+ praisonai flow export abc-123-def
274+ praisonai flow export abc-123-def --output my_workflow.yaml
275+ praisonai flow export abc-123-def --format json
276+ """
277+ from pathlib import Path
278+ from rich .console import Console
279+
280+ console = Console ()
281+
282+ try :
283+ from praisonai .flow .client import create_client
284+
285+ console .print (f"[cyan]Connecting to Langflow at { langflow_url } ...[/cyan]" )
286+ client = create_client (langflow_url )
287+
288+ # Check server health
289+ health = client .health_check ()
290+ if health ["status" ] != "healthy" :
291+ console .print (f"[red]Error: Langflow server not accessible at { langflow_url } [/red]" )
292+ raise typer .Abort ()
293+
294+ # Download flow
295+ console .print (f"[cyan]Downloading flow { flow_id } ...[/cyan]" )
296+ flow_data = client .get_flow (flow_id )
297+
298+ # Determine output file
299+ if not output :
300+ flow_name = flow_data .get ("name" , flow_id )
301+ safe_name = "" .join (c for c in flow_name if c .isalnum () or c in (' ' , '-' , '_' )).rstrip ()
302+ output = f"{ safe_name } .{ format } "
303+
304+ output_path = Path (output )
305+ output_path .parent .mkdir (parents = True , exist_ok = True )
306+
307+ if format == "json" :
308+ # Save as JSON
309+ import json
310+ with open (output_path , 'w' ) as f :
311+ json .dump (flow_data , f , indent = 2 )
312+ else :
313+ # Convert to YAML
314+ from praisonai .flow .converter import langflow_json_to_yaml
315+
316+ console .print ("[cyan]Converting to YAML format...[/cyan]" )
317+ yaml_content = langflow_json_to_yaml (flow_data )
318+
319+ with open (output_path , 'w' ) as f :
320+ f .write (yaml_content )
321+
322+ console .print (f"[green]✅ Flow exported to { output_path } [/green]" )
323+
324+ except ImportError as e :
325+ console .print (f"[red]Error: { e } [/red]" )
326+ console .print ("[yellow]Install with: pip install 'praisonai[flow]'[/yellow]" )
327+ raise typer .Abort ()
328+ except Exception as e :
329+ console .print (f"[red]Error exporting flow: { e } [/red]" )
330+ raise typer .Abort ()
331+
332+
333+ @app .command ("list" )
334+ def flow_list (
335+ langflow_url : str = typer .Option (
336+ "http://localhost:7860" ,
337+ "--url" ,
338+ help = "Langflow server URL"
339+ ),
340+ search : str = typer .Option (
341+ None ,
342+ "--search" ,
343+ "-s" ,
344+ help = "Search flows by name or description"
345+ ),
346+ ):
347+ """List flows in Langflow server.
348+
349+ Shows all flows with their IDs, names, and descriptions for
350+ easy identification and export.
351+
352+ Examples:
353+ praisonai flow list
354+ praisonai flow list --search research
355+ praisonai flow list --url http://localhost:8080
356+ """
357+ from rich .console import Console
358+ from rich .table import Table
359+
360+ console = Console ()
361+
362+ try :
363+ from praisonai .flow .client import create_client
364+
365+ console .print (f"[cyan]Connecting to Langflow at { langflow_url } ...[/cyan]" )
366+ client = create_client (langflow_url )
367+
368+ # Check server health
369+ health = client .health_check ()
370+ if health ["status" ] != "healthy" :
371+ console .print (f"[red]Error: Langflow server not accessible at { langflow_url } [/red]" )
372+ raise typer .Abort ()
373+
374+ # Get flows
375+ if search :
376+ console .print (f"[cyan]Searching for flows matching '{ search } '...[/cyan]" )
377+ flows = client .search_flows (search )
378+ else :
379+ console .print ("[cyan]Loading flows...[/cyan]" )
380+ flows = client .list_flows ()
381+
382+ if not flows :
383+ if search :
384+ console .print (f"[yellow]No flows found matching '{ search } '[/yellow]" )
385+ else :
386+ console .print ("[yellow]No flows found[/yellow]" )
387+ return
388+
389+ # Create table
390+ table = Table (title = f"Langflow Flows ({ len (flows )} found)" )
391+ table .add_column ("ID" , style = "cyan" , min_width = 20 )
392+ table .add_column ("Name" , style = "green" )
393+ table .add_column ("Description" , style = "dim" )
394+ table .add_column ("Created" , style = "blue" )
395+
396+ for flow in flows :
397+ flow_id = flow .get ("id" , "" )[:20 ] + "..." if len (flow .get ("id" , "" )) > 20 else flow .get ("id" , "" )
398+ name = flow .get ("name" , "Unnamed" )
399+ description = flow .get ("description" , "" )[:50 ] + "..." if len (flow .get ("description" , "" )) > 50 else flow .get ("description" , "" )
400+ created = flow .get ("created_at" , "" )[:10 ] if flow .get ("created_at" ) else ""
401+
402+ table .add_row (flow_id , name , description , created )
403+
404+ console .print (table )
405+
406+ except ImportError as e :
407+ console .print (f"[red]Error: { e } [/red]" )
408+ console .print ("[yellow]Install with: pip install 'praisonai[flow]'[/yellow]" )
409+ raise typer .Abort ()
410+ except Exception as e :
411+ console .print (f"[red]Error listing flows: { e } [/red]" )
412+ raise typer .Abort ()
413+
414+
129415@app .command ("version" )
130416def flow_version ():
131417 """Show Langflow version information."""
0 commit comments