lfx-nightly 0.1.12.dev33__py3-none-any.whl → 0.1.12.dev35__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of lfx-nightly might be problematic. Click here for more details.

lfx/cli/commands.py CHANGED
@@ -85,6 +85,11 @@ def serve_command(
85
85
  cat my_flow.json | lfx serve --stdin
86
86
  echo '{"nodes": [...]}' | lfx serve --stdin
87
87
  """
88
+ # Configure logging with the specified level and import logger
89
+ from lfx.log.logger import configure, logger
90
+
91
+ configure(log_level=log_level)
92
+
88
93
  verbose_print = create_verbose_printer(verbose=verbose)
89
94
 
90
95
  # Validate input sources - exactly one must be provided
@@ -134,11 +139,11 @@ def serve_command(
134
139
  temp_file_to_cleanup = None
135
140
 
136
141
  if flow_json is not None:
137
- verbose_print("Processing inline JSON content...")
142
+ logger.info("Processing inline JSON content...")
138
143
  try:
139
144
  # Validate JSON syntax
140
145
  json_data = json.loads(flow_json)
141
- verbose_print("JSON content is valid")
146
+ logger.info("JSON content is valid")
142
147
 
143
148
  # Create a temporary file with the JSON content
144
149
  with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as temp_file:
@@ -146,7 +151,7 @@ def serve_command(
146
151
  temp_file_to_cleanup = temp_file.name
147
152
 
148
153
  script_path = temp_file_to_cleanup
149
- verbose_print(f"Created temporary file: {script_path}")
154
+ logger.info(f"Created temporary file: {script_path}")
150
155
 
151
156
  except json.JSONDecodeError as e:
152
157
  typer.echo(f"Error: Invalid JSON content: {e}", err=True)
@@ -156,17 +161,17 @@ def serve_command(
156
161
  raise typer.Exit(1) from e
157
162
 
158
163
  elif stdin:
159
- verbose_print("Reading JSON content from stdin...")
164
+ logger.info("Reading JSON content from stdin...")
160
165
  try:
161
166
  # Read all content from stdin
162
167
  stdin_content = sys.stdin.read().strip()
163
168
  if not stdin_content:
164
- verbose_print("Error: No content received from stdin")
169
+ logger.error("No content received from stdin")
165
170
  raise typer.Exit(1)
166
171
 
167
172
  # Validate JSON syntax
168
173
  json_data = json.loads(stdin_content)
169
- verbose_print("JSON content from stdin is valid")
174
+ logger.info("JSON content from stdin is valid")
170
175
 
171
176
  # Create a temporary file with the JSON content
172
177
  with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as temp_file:
@@ -174,7 +179,7 @@ def serve_command(
174
179
  temp_file_to_cleanup = temp_file.name
175
180
 
176
181
  script_path = temp_file_to_cleanup
177
- verbose_print(f"Created temporary file from stdin: {script_path}")
182
+ logger.info(f"Created temporary file from stdin: {script_path}")
178
183
 
179
184
  except json.JSONDecodeError as e:
180
185
  verbose_print(f"Error: Invalid JSON content from stdin: {e}")
@@ -210,10 +215,10 @@ def serve_command(
210
215
  raise typer.Exit(1)
211
216
 
212
217
  # Prepare the graph
213
- verbose_print("Preparing graph for serving...")
218
+ logger.info("Preparing graph for serving...")
214
219
  try:
215
220
  graph.prepare()
216
- verbose_print("Graph prepared successfully")
221
+ logger.info("Graph prepared successfully")
217
222
 
218
223
  # Validate global variables for environment compatibility
219
224
  if check_variables:
@@ -221,12 +226,12 @@ def serve_command(
221
226
 
222
227
  validation_errors = validate_global_variables_for_env(graph)
223
228
  if validation_errors:
224
- verbose_print("Global variable validation failed:")
229
+ logger.error("Global variable validation failed:")
225
230
  for error in validation_errors:
226
- verbose_print(f" - {error}")
231
+ logger.error(f" - {error}")
227
232
  raise typer.Exit(1)
228
233
  else:
229
- verbose_print("Global variable validation skipped")
234
+ logger.info("Global variable validation skipped")
230
235
  except Exception as e:
231
236
  verbose_print(f"✗ Failed to prepare graph: {e}")
232
237
  raise typer.Exit(1) from e
lfx/cli/run.py CHANGED
@@ -18,18 +18,29 @@ from lfx.cli.validation import validate_global_variables_for_env
18
18
  from lfx.log.logger import logger
19
19
  from lfx.schema.schema import InputValueRequest
20
20
 
21
+ # Verbosity level constants
22
+ VERBOSITY_DETAILED = 2
23
+ VERBOSITY_FULL = 3
21
24
 
22
- def output_error(error_message: str, *, verbose: bool) -> None:
25
+
26
+ def output_error(error_message: str, *, verbose: bool, exception: Exception | None = None) -> None:
23
27
  """Output error in JSON format to stdout when not verbose, or to stderr when verbose."""
24
28
  if verbose:
25
29
  typer.echo(f"{error_message}", file=sys.stderr)
30
+
31
+ error_response = {
32
+ "success": False,
33
+ "type": "error",
34
+ }
35
+
36
+ # Add clean exception data if available
37
+ if exception:
38
+ error_response["exception_type"] = type(exception).__name__
39
+ error_response["exception_message"] = str(exception)
26
40
  else:
27
- error_response = {
28
- "success": False,
29
- "error": error_message,
30
- "type": "error",
31
- }
32
- typer.echo(json.dumps(error_response))
41
+ error_response["exception_message"] = error_message
42
+
43
+ typer.echo(json.dumps(error_response))
33
44
 
34
45
 
35
46
  @partial(syncify, raise_sync_error=False)
@@ -67,9 +78,20 @@ async def run(
67
78
  help="Check global variables for environment compatibility",
68
79
  ),
69
80
  verbose: bool = typer.Option(
70
- default=False,
71
- show_default=True,
72
- help="Show diagnostic output and execution details",
81
+ False, # noqa: FBT003
82
+ "-v",
83
+ "--verbose",
84
+ help="Show basic progress information",
85
+ ),
86
+ verbose_detailed: bool = typer.Option(
87
+ False, # noqa: FBT003
88
+ "-vv",
89
+ help="Show detailed progress and debug information",
90
+ ),
91
+ verbose_full: bool = typer.Option(
92
+ False, # noqa: FBT003
93
+ "-vvv",
94
+ help="Show full debugging output including component logs",
73
95
  ),
74
96
  timing: bool = typer.Option(
75
97
  default=False,
@@ -88,6 +110,8 @@ async def run(
88
110
  input_value: Input value to pass to the graph (positional argument)
89
111
  input_value_option: Input value to pass to the graph (alternative option)
90
112
  verbose: Show diagnostic output and execution details
113
+ verbose_detailed: Show detailed progress and debug information (-vv)
114
+ verbose_full: Show full debugging output including component logs (-vvv)
91
115
  output_format: Format for output (json, text, message, or result)
92
116
  flow_json: Inline JSON flow content as a string
93
117
  stdin: Read JSON flow content from stdin
@@ -96,15 +120,22 @@ async def run(
96
120
  """
97
121
  # Start timing if requested
98
122
  import time
99
- from datetime import datetime
100
-
101
- def verbose_print(message: str, level: str = "INFO") -> None:
102
- if verbose:
103
- timestamp = datetime.now().strftime("%H:%M:%S.%f")[:-3] # Include milliseconds # noqa: DTZ005
104
- typer.echo(f"[{timestamp}] {level}: {message}", file=sys.stderr)
105
123
 
106
- def debug_print(message: str) -> None:
107
- verbose_print(message, level="DEBUG")
124
+ # Configure logger based on verbosity level
125
+ from lfx.log.logger import configure
126
+
127
+ if verbose_full:
128
+ configure(log_level="DEBUG", output_file=sys.stderr) # Show everything including component debug logs
129
+ verbosity = 3
130
+ elif verbose_detailed:
131
+ configure(log_level="DEBUG", output_file=sys.stderr) # Show debug and above
132
+ verbosity = 2
133
+ elif verbose:
134
+ configure(log_level="INFO", output_file=sys.stderr) # Show info and above including our CLI info messages
135
+ verbosity = 1
136
+ else:
137
+ configure(log_level="CRITICAL", output_file=sys.stderr) # Only critical errors
138
+ verbosity = 0
108
139
 
109
140
  start_time = time.time() if timing else None
110
141
 
@@ -127,15 +158,18 @@ async def run(
127
158
  temp_file_to_cleanup = None
128
159
 
129
160
  if flow_json is not None:
130
- verbose_print("Processing inline JSON content...")
161
+ if verbosity > 0:
162
+ typer.echo("Processing inline JSON content...", file=sys.stderr)
131
163
  try:
132
164
  json_data = json.loads(flow_json)
133
- verbose_print("JSON content is valid")
165
+ if verbosity > 0:
166
+ typer.echo("JSON content is valid", file=sys.stderr)
134
167
  with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as temp_file:
135
168
  json.dump(json_data, temp_file, indent=2)
136
169
  temp_file_to_cleanup = temp_file.name
137
170
  script_path = Path(temp_file_to_cleanup)
138
- verbose_print(f"Created temporary file: {script_path}")
171
+ if verbosity > 0:
172
+ typer.echo(f"Created temporary file: {script_path}", file=sys.stderr)
139
173
  except json.JSONDecodeError as e:
140
174
  output_error(f"Invalid JSON content: {e}", verbose=verbose)
141
175
  raise typer.Exit(1) from e
@@ -143,19 +177,22 @@ async def run(
143
177
  output_error(f"Error processing JSON content: {e}", verbose=verbose)
144
178
  raise typer.Exit(1) from e
145
179
  elif stdin:
146
- verbose_print("Reading JSON content from stdin...")
180
+ if verbosity > 0:
181
+ typer.echo("Reading JSON content from stdin...", file=sys.stderr)
147
182
  try:
148
183
  stdin_content = sys.stdin.read().strip()
149
184
  if not stdin_content:
150
185
  output_error("No content received from stdin", verbose=verbose)
151
186
  raise typer.Exit(1)
152
187
  json_data = json.loads(stdin_content)
153
- verbose_print("JSON content from stdin is valid")
188
+ if verbosity > 0:
189
+ typer.echo("JSON content from stdin is valid", file=sys.stderr)
154
190
  with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as temp_file:
155
191
  json.dump(json_data, temp_file, indent=2)
156
192
  temp_file_to_cleanup = temp_file.name
157
193
  script_path = Path(temp_file_to_cleanup)
158
- verbose_print(f"Created temporary file from stdin: {script_path}")
194
+ if verbosity > 0:
195
+ typer.echo(f"Created temporary file from stdin: {script_path}", file=sys.stderr)
159
196
  except json.JSONDecodeError as e:
160
197
  output_error(f"Invalid JSON content from stdin: {e}", verbose=verbose)
161
198
  raise typer.Exit(1) from e
@@ -175,7 +212,8 @@ async def run(
175
212
  error_msg = f"'{script_path}' must be a .py or .json file."
176
213
  raise ValueError(error_msg)
177
214
  file_type = "Python script" if file_extension == ".py" else "JSON flow"
178
- verbose_print(f"Analyzing {file_type}: {script_path}")
215
+ if verbosity > 0:
216
+ typer.echo(f"Analyzing {file_type}: {script_path}", file=sys.stderr)
179
217
  if file_extension == ".py":
180
218
  graph_info = find_graph_variable(script_path)
181
219
  if not graph_info:
@@ -183,47 +221,44 @@ async def run(
183
221
  "No 'graph' variable found in the script. Expected to find an assignment like: graph = Graph(...)"
184
222
  )
185
223
  raise ValueError(error_msg)
186
- verbose_print(f"Found 'graph' variable at line {graph_info['line_number']}")
187
- verbose_print(f"Type: {graph_info['type']}")
188
- verbose_print(f"Source: {graph_info['source_line']}")
189
- verbose_print("Loading and executing script...")
224
+ if verbosity > 0:
225
+ typer.echo(f"Found 'graph' variable at line {graph_info['line_number']}", file=sys.stderr)
226
+ typer.echo(f"Type: {graph_info['type']}", file=sys.stderr)
227
+ typer.echo(f"Source: {graph_info['source_line']}", file=sys.stderr)
228
+ typer.echo("Loading and executing script...", file=sys.stderr)
190
229
  graph = load_graph_from_script(script_path)
191
230
  elif file_extension == ".json":
192
- verbose_print("Valid JSON flow file detected")
193
- verbose_print("\nLoading and executing JSON flow...")
231
+ if verbosity > 0:
232
+ typer.echo("Valid JSON flow file detected", file=sys.stderr)
233
+ typer.echo("Loading and executing JSON flow", file=sys.stderr)
194
234
  from lfx.load import aload_flow_from_json
195
235
 
196
236
  graph = await aload_flow_from_json(script_path, disable_logs=not verbose)
197
237
  except Exception as e:
198
238
  error_type = type(e).__name__
199
- verbose_print(f"Graph loading failed with {error_type}", level="ERROR")
200
-
201
- if verbose:
202
- # Enhanced error context for better debugging
203
- debug_print(f"Exception type: {error_type}")
204
- debug_print(f"Exception message: {e!s}")
239
+ logger.error(f"Graph loading failed with {error_type}")
205
240
 
241
+ if verbosity > 0:
206
242
  # Try to identify common error patterns
207
243
  if "ModuleNotFoundError" in str(e) or "No module named" in str(e):
208
- verbose_print("This appears to be a missing dependency issue", level="WARN")
244
+ logger.info("This appears to be a missing dependency issue")
209
245
  if "langchain" in str(e).lower():
210
- verbose_print(
246
+ logger.info(
211
247
  "Missing LangChain dependency detected. Try: pip install langchain-<provider>",
212
- level="WARN",
213
248
  )
214
249
  elif "ImportError" in str(e):
215
- verbose_print("This appears to be an import issue - check component dependencies", level="WARN")
250
+ logger.info("This appears to be an import issue - check component dependencies")
216
251
  elif "AttributeError" in str(e):
217
- verbose_print("This appears to be a component configuration issue", level="WARN")
252
+ logger.info("This appears to be a component configuration issue")
218
253
 
219
254
  # Show full traceback in debug mode
220
- logger.exception("Failed to load graph - full traceback:")
255
+ logger.exception("Failed to load graph.")
221
256
 
222
- output_error(f"Failed to load graph: {e}", verbose=verbose)
257
+ output_error(f"Failed to load graph. {e}", verbose=verbose, exception=e)
223
258
  if temp_file_to_cleanup:
224
259
  try:
225
260
  Path(temp_file_to_cleanup).unlink()
226
- verbose_print(f"Cleaned up temporary file: {temp_file_to_cleanup}", level="SUCCESS")
261
+ logger.info(f"Cleaned up temporary file: {temp_file_to_cleanup}")
227
262
  except OSError:
228
263
  pass
229
264
  raise typer.Exit(1) from e
@@ -233,70 +268,70 @@ async def run(
233
268
  # Mark end of loading phase if timing
234
269
  load_end_time = time.time() if timing else None
235
270
 
236
- verbose_print("Preparing graph for execution...")
271
+ if verbosity > 0:
272
+ typer.echo("Preparing graph for execution...", file=sys.stderr)
237
273
  try:
238
274
  # Add detailed preparation steps
239
- if verbose:
240
- debug_print(f"Graph contains {len(graph.vertices)} vertices")
241
- debug_print(f"Graph contains {len(graph.edges)} edges")
275
+ if verbosity > 0:
276
+ logger.debug(f"Graph contains {len(graph.vertices)} vertices")
277
+ logger.debug(f"Graph contains {len(graph.edges)} edges")
242
278
 
243
279
  # Show component types being used
244
280
  component_types = set()
245
281
  for vertex in graph.vertices:
246
282
  if hasattr(vertex, "display_name"):
247
283
  component_types.add(vertex.display_name)
248
- debug_print(f"Component types in graph: {', '.join(sorted(component_types))}")
284
+ logger.debug(f"Component types in graph: {', '.join(sorted(component_types))}")
249
285
 
250
286
  graph.prepare()
251
- verbose_print("Graph preparation completed", level="SUCCESS")
287
+ logger.info("Graph preparation completed")
252
288
 
253
289
  # Validate global variables for environment compatibility
254
290
  if check_variables:
255
- verbose_print("Validating global variables...")
291
+ logger.info("Validating global variables...")
256
292
  validation_errors = validate_global_variables_for_env(graph)
257
293
  if validation_errors:
258
294
  error_details = "Global variable validation failed: " + "; ".join(validation_errors)
259
- verbose_print(f"Variable validation failed: {len(validation_errors)} errors", level="ERROR")
295
+ logger.info(f"Variable validation failed: {len(validation_errors)} errors")
260
296
  for error in validation_errors:
261
- debug_print(f"Validation error: {error}")
297
+ logger.debug(f"Validation error: {error}")
262
298
  output_error(error_details, verbose=verbose)
263
299
  if temp_file_to_cleanup:
264
300
  try:
265
301
  Path(temp_file_to_cleanup).unlink()
266
- verbose_print(f"Cleaned up temporary file: {temp_file_to_cleanup}", level="SUCCESS")
302
+ logger.info(f"Cleaned up temporary file: {temp_file_to_cleanup}")
267
303
  except OSError:
268
304
  pass
269
305
  if validation_errors:
270
306
  raise typer.Exit(1)
271
- verbose_print("Global variable validation passed", level="SUCCESS")
307
+ logger.info("Global variable validation passed")
272
308
  else:
273
- verbose_print("Global variable validation skipped", level="SUCCESS")
309
+ logger.info("Global variable validation skipped")
274
310
  except Exception as e:
275
311
  error_type = type(e).__name__
276
- verbose_print(f"Graph preparation failed with {error_type}", level="ERROR")
312
+ logger.info(f"Graph preparation failed with {error_type}")
277
313
 
278
- if verbose:
279
- debug_print(f"Preparation error: {e!s}")
314
+ if verbosity > 0:
315
+ logger.debug(f"Preparation error: {e!s}")
280
316
  logger.exception("Failed to prepare graph - full traceback:")
281
317
 
282
- output_error(f"Failed to prepare graph: {e}", verbose=verbose)
318
+ output_error(f"Failed to prepare graph: {e}", verbose=verbose, exception=e)
283
319
  if temp_file_to_cleanup:
284
320
  try:
285
321
  Path(temp_file_to_cleanup).unlink()
286
- verbose_print(f"Cleaned up temporary file: {temp_file_to_cleanup}")
322
+ logger.info(f"Cleaned up temporary file: {temp_file_to_cleanup}")
287
323
  except OSError:
288
324
  pass
289
325
  raise typer.Exit(1) from e
290
326
 
291
- verbose_print("Executing graph...")
327
+ logger.info("Executing graph...")
292
328
  execution_start_time = time.time() if timing else None
293
-
294
329
  if verbose:
295
- debug_print("Setting up execution environment")
330
+ logger.debug("Setting up execution environment")
296
331
  if inputs:
297
- debug_print(f"Input provided: {inputs.input_value}")
332
+ logger.debug(f"Input provided: {inputs.input_value}")
298
333
  else:
299
- debug_print("No input provided")
334
+ logger.debug("No input provided")
300
335
 
301
336
  captured_stdout = StringIO()
302
337
  captured_stderr = StringIO()
@@ -309,18 +344,20 @@ async def run(
309
344
 
310
345
  try:
311
346
  sys.stdout = captured_stdout
312
- sys.stderr = captured_stderr
347
+ # Don't capture stderr at high verbosity levels to avoid duplication with direct logging
348
+ if verbosity < VERBOSITY_FULL:
349
+ sys.stderr = captured_stderr
313
350
  results = []
314
351
 
315
- verbose_print("Starting graph execution...", level="DEBUG")
352
+ logger.info("Starting graph execution...", level="DEBUG")
316
353
  result_count = 0
317
354
 
318
355
  async for result in graph.async_start(inputs):
319
356
  result_count += 1
320
- if verbose:
321
- debug_print(f"Processing result #{result_count}")
357
+ if verbosity > 0:
358
+ logger.debug(f"Processing result #{result_count}")
322
359
  if hasattr(result, "vertex") and hasattr(result.vertex, "display_name"):
323
- debug_print(f"Component: {result.vertex.display_name}")
360
+ logger.debug(f"Component: {result.vertex.display_name}")
324
361
  if timing:
325
362
  step_end_time = time.time()
326
363
  step_duration = step_end_time - execution_step_start
@@ -342,46 +379,70 @@ async def run(
342
379
 
343
380
  results.append(result)
344
381
 
345
- verbose_print(f"Graph execution completed. Processed {result_count} results", level="SUCCESS")
382
+ logger.info(f"Graph execution completed. Processed {result_count} results")
346
383
 
347
384
  except Exception as e:
348
- sys.stdout = original_stdout
349
- sys.stderr = original_stderr
350
-
351
385
  error_type = type(e).__name__
352
- verbose_print(f"Graph execution failed with {error_type}", level="ERROR")
386
+ logger.info(f"Graph execution failed with {error_type}")
353
387
 
354
- if verbose:
355
- debug_print(f"Execution error: {e!s}")
356
- debug_print(f"Failed after processing {result_count} results")
388
+ if verbosity >= VERBOSITY_DETAILED: # Only show details at -vv and above
389
+ logger.debug(f"Failed after processing {result_count} results")
357
390
 
391
+ # Only show component output at maximum verbosity (-vvv)
392
+ if verbosity >= VERBOSITY_FULL:
358
393
  # Capture any output that was generated before the error
359
- captured_content = captured_stdout.getvalue() + captured_stderr.getvalue()
394
+ # Only show captured stdout since stderr logging is already shown directly in verbose mode
395
+ captured_content = captured_stdout.getvalue()
360
396
  if captured_content.strip():
361
- debug_print("Captured output before error:")
362
- for line in captured_content.strip().split("\n"):
363
- debug_print(f" | {line}")
397
+ # Check if captured content contains the same error that will be displayed at the end
398
+ error_text = str(e)
399
+ captured_lines = captured_content.strip().split("\n")
400
+
401
+ # Filter out lines that are duplicates of the final error message
402
+ unique_lines = [
403
+ line
404
+ for line in captured_lines
405
+ if not any(
406
+ error_part.strip() in line for error_part in error_text.split("\n") if error_part.strip()
407
+ )
408
+ ]
409
+
410
+ if unique_lines:
411
+ logger.info("Component output before error:", level="DEBUG")
412
+ for line in unique_lines:
413
+ # Log each line directly using the logger to avoid nested formatting
414
+ if verbosity > 0:
415
+ # Remove any existing timestamp prefix to avoid duplication
416
+ clean_line = line
417
+ if "] " in line and line.startswith("2025-"):
418
+ # Extract just the log message after the timestamp and level
419
+ parts = line.split("] ", 1)
420
+ if len(parts) > 1:
421
+ clean_line = parts[1]
422
+ logger.debug(clean_line)
364
423
 
365
424
  # Provide context about common execution errors
366
425
  if "list can't be used in 'await' expression" in str(e):
367
- verbose_print("This appears to be an async/await mismatch in a component", level="WARN")
368
- verbose_print("Check that async methods are properly awaited", level="WARN")
426
+ logger.info("This appears to be an async/await mismatch in a component")
427
+ logger.info("Check that async methods are properly awaited")
369
428
  elif "AttributeError" in error_type and "NoneType" in str(e):
370
- verbose_print("This appears to be a null reference error", level="WARN")
371
- verbose_print("A component may be receiving unexpected None values", level="WARN")
429
+ logger.info("This appears to be a null reference error")
430
+ logger.info("A component may be receiving unexpected None values")
372
431
  elif "ConnectionError" in str(e) or "TimeoutError" in str(e):
373
- verbose_print("This appears to be a network connectivity issue", level="WARN")
374
- verbose_print("Check API keys and network connectivity", level="WARN")
432
+ logger.info("This appears to be a network connectivity issue")
433
+ logger.info("Check API keys and network connectivity")
375
434
 
376
435
  logger.exception("Failed to execute graph - full traceback:")
377
436
 
378
- output_error(f"Failed to execute graph: {e}", verbose=verbose)
379
437
  if temp_file_to_cleanup:
380
438
  try:
381
439
  Path(temp_file_to_cleanup).unlink()
382
- verbose_print(f"Cleaned up temporary file: {temp_file_to_cleanup}", level="SUCCESS")
440
+ logger.info(f"Cleaned up temporary file: {temp_file_to_cleanup}")
383
441
  except OSError:
384
442
  pass
443
+ sys.stdout = original_stdout
444
+ sys.stderr = original_stderr
445
+ output_error(f"Failed to execute graph: {e}", verbose=verbosity > 0, exception=e)
385
446
  raise typer.Exit(1) from e
386
447
  finally:
387
448
  sys.stdout = original_stdout
@@ -389,7 +450,7 @@ async def run(
389
450
  if temp_file_to_cleanup:
390
451
  try:
391
452
  Path(temp_file_to_cleanup).unlink()
392
- verbose_print(f"Cleaned up temporary file: {temp_file_to_cleanup}")
453
+ logger.info(f"Cleaned up temporary file: {temp_file_to_cleanup}")
393
454
  except OSError:
394
455
  pass
395
456
 
@@ -424,7 +485,7 @@ async def run(
424
485
  result_data["logs"] = captured_logs
425
486
  if timing_metadata:
426
487
  result_data["timing"] = timing_metadata
427
- indent = 2 if verbose else None
488
+ indent = 2 if verbosity > 0 else None
428
489
  typer.echo(json.dumps(result_data, indent=indent))
429
490
  elif output_format in {"text", "message"}:
430
491
  result_data = extract_structured_result(results)
@@ -437,5 +498,5 @@ async def run(
437
498
  result_data["logs"] = captured_logs
438
499
  if timing_metadata:
439
500
  result_data["timing"] = timing_metadata
440
- indent = 2 if verbose else None
501
+ indent = 2 if verbosity > 0 else None
441
502
  typer.echo(json.dumps(result_data, indent=indent))
@@ -7,7 +7,12 @@ from typing import Any
7
7
  from langchain_core.tools import StructuredTool # noqa: TC002
8
8
 
9
9
  from lfx.base.agents.utils import maybe_unflatten_dict, safe_cache_get, safe_cache_set
10
- from lfx.base.mcp.util import MCPSseClient, MCPStdioClient, create_input_schema_from_json_schema, update_tools
10
+ from lfx.base.mcp.util import (
11
+ MCPStdioClient,
12
+ MCPStreamableHttpClient,
13
+ create_input_schema_from_json_schema,
14
+ update_tools,
15
+ )
11
16
  from lfx.custom.custom_component.component_with_cache import ComponentWithCache
12
17
  from lfx.inputs.inputs import InputTypes # noqa: TC001
13
18
  from lfx.io import BoolInput, DropdownInput, McpInput, MessageTextInput, Output
@@ -32,7 +37,9 @@ class MCPToolsComponent(ComponentWithCache):
32
37
 
33
38
  # Initialize clients with access to the component cache
34
39
  self.stdio_client: MCPStdioClient = MCPStdioClient(component_cache=self._shared_component_cache)
35
- self.sse_client: MCPSseClient = MCPSseClient(component_cache=self._shared_component_cache)
40
+ self.streamable_http_client: MCPStreamableHttpClient = MCPStreamableHttpClient(
41
+ component_cache=self._shared_component_cache
42
+ )
36
43
 
37
44
  def _ensure_cache_structure(self):
38
45
  """Ensure the cache has the required structure."""
@@ -207,7 +214,7 @@ class MCPToolsComponent(ComponentWithCache):
207
214
  server_name=server_name,
208
215
  server_config=server_config,
209
216
  mcp_stdio_client=self.stdio_client,
210
- mcp_sse_client=self.sse_client,
217
+ mcp_streamable_http_client=self.streamable_http_client,
211
218
  )
212
219
 
213
220
  self.tool_names = [tool.name for tool in tool_list if hasattr(tool, "name")]
@@ -496,7 +503,7 @@ class MCPToolsComponent(ComponentWithCache):
496
503
  session_context = self._get_session_context()
497
504
  if session_context:
498
505
  self.stdio_client.set_session_context(session_context)
499
- self.sse_client.set_session_context(session_context)
506
+ self.streamable_http_client.set_session_context(session_context)
500
507
 
501
508
  exec_tool = self._tool_cache[self.tool]
502
509
  tool_args = self.get_inputs_for_all_tools(self.tools)[self.tool]
@@ -4,7 +4,7 @@ from lfx.base.flow_processing.utils import build_data_from_result_data
4
4
  from lfx.custom.custom_component.custom_component import CustomComponent
5
5
  from lfx.graph.graph.base import Graph
6
6
  from lfx.graph.vertex.base import Vertex
7
- from lfx.helpers.flow import get_flow_inputs
7
+ from lfx.helpers import get_flow_inputs
8
8
  from lfx.log.logger import logger
9
9
  from lfx.schema.data import Data
10
10
  from lfx.schema.dotdict import dotdict
@@ -6,7 +6,7 @@ from lfx.base.langchain_utilities.model import LCToolComponent
6
6
  from lfx.base.tools.flow_tool import FlowTool
7
7
  from lfx.field_typing import Tool
8
8
  from lfx.graph.graph.base import Graph
9
- from lfx.helpers.flow import get_flow_inputs
9
+ from lfx.helpers import get_flow_inputs
10
10
  from lfx.io import BoolInput, DropdownInput, Output, StrInput
11
11
  from lfx.log.logger import logger
12
12
  from lfx.schema.data import Data
@@ -1,7 +1,7 @@
1
1
  from typing import Any
2
2
 
3
3
  from lfx.base.tools.run_flow import RunFlowBaseComponent
4
- from lfx.helpers.flow import run_flow
4
+ from lfx.helpers import run_flow
5
5
  from lfx.log.logger import logger
6
6
  from lfx.schema.dotdict import dotdict
7
7
 
@@ -4,7 +4,7 @@ from lfx.base.flow_processing.utils import build_data_from_result_data
4
4
  from lfx.custom.custom_component.component import Component
5
5
  from lfx.graph.graph.base import Graph
6
6
  from lfx.graph.vertex.base import Vertex
7
- from lfx.helpers.flow import get_flow_inputs
7
+ from lfx.helpers import get_flow_inputs
8
8
  from lfx.io import DropdownInput, Output
9
9
  from lfx.log.logger import logger
10
10
  from lfx.schema.data import Data
@@ -24,11 +24,8 @@ class NVIDIAModelComponent(LCModelComponent):
24
24
  except ImportError as e:
25
25
  msg = "Please install langchain-nvidia-ai-endpoints to use the NVIDIA model."
26
26
  raise ImportError(msg) from e
27
- except Exception: # noqa: BLE001
28
- logger.warning(
29
- "Failed to connect to NVIDIA API. Model list may be unavailable."
30
- " Please check your internet connection and API credentials."
31
- )
27
+ except Exception as e: # noqa: BLE001
28
+ logger.warning(f"Failed to fetch NVIDIA models during initialization: {e}. Model list will be unavailable.")
32
29
  all_models = []
33
30
 
34
31
  inputs = [