diff --git a/src/backend/base/langflow/__main__.py b/src/backend/base/langflow/__main__.py index 2527c4f08..553eb2fd6 100644 --- a/src/backend/base/langflow/__main__.py +++ b/src/backend/base/langflow/__main__.py @@ -47,6 +47,10 @@ class ProcessManager: def __init__(self): self.webapp_process = None self.shutdown_in_progress = False + if platform.system() == "Windows": + self._farewell_emoji = ":)" # ASCII smiley + else: + self._farewell_emoji = "👋" # Unicode wave # params are required for signal handlers, even if they are not used def handle_sigterm(self, _signum: int, _frame) -> None: @@ -78,8 +82,21 @@ class ProcessManager: logger.warning("Process didn't terminate gracefully, killing it.") self.webapp_process.kill() self.webapp_process.join() + self.print_farewell_message() + sys.exit(0) + def print_farewell_message(self) -> None: + """Print a nice farewell message after shutdown is complete.""" + # Clear any progress indicator output that might be on the current line + sys.stdout.write("\r") # Move cursor to beginning of line + sys.stdout.write(" " * 80) # Clear the line with spaces + sys.stdout.write("\r") # Move cursor back to beginning + + click.echo() + farewell = click.style(f"{self._farewell_emoji} See you next time!", fg="bright_blue", bold=True) + click.echo(farewell) + # Create a single instance of ProcessManager process_manager = ProcessManager() diff --git a/src/backend/base/langflow/cli/progress.py b/src/backend/base/langflow/cli/progress.py index d0a455c01..5a618aea0 100644 --- a/src/backend/base/langflow/cli/progress.py +++ b/src/backend/base/langflow/cli/progress.py @@ -178,12 +178,6 @@ class ProgressIndicator: click.echo() click.echo(click.style(f"Total shutdown time: {total_time:.2f}s", fg="bright_black")) - def print_farewell_message(self) -> None: - """Print a nice farewell message after shutdown is complete.""" - click.echo() - farewell = click.style(f"{self._farewell_emoji} See you next time!", fg="bright_blue", bold=True) - click.echo(farewell) - def create_langflow_progress(*, verbose: bool = False) -> ProgressIndicator: """Create a progress indicator with predefined Langflow initialization steps.""" @@ -206,18 +200,32 @@ def create_langflow_progress(*, verbose: bool = False) -> ProgressIndicator: return progress -def create_langflow_shutdown_progress(*, verbose: bool = False) -> ProgressIndicator: +def create_langflow_shutdown_progress(*, verbose: bool = False, multiple_workers: bool = False) -> ProgressIndicator: """Create a progress indicator with predefined Langflow shutdown steps.""" progress = ProgressIndicator(verbose=verbose) # Define the shutdown steps in reverse order of initialization - steps = [ - ("Stopping Server", "Gracefully stopping the web server"), - ("Cancelling Background Tasks", "Stopping file synchronization and background jobs"), - ("Cleaning Up Services", "Teardown database connections and services"), - ("Clearing Temporary Files", "Removing temporary directories and cache"), - ("Finalizing Shutdown", "Completing cleanup and logging"), - ] + if multiple_workers: + import os + + steps = [ + (f"[Worker PID {os.getpid()}] Stopping Server", "Gracefully stopping the web server"), + ( + f"[Worker PID {os.getpid()}] Cancelling Background Tasks", + "Stopping file synchronization and background jobs", + ), + (f"[Worker PID {os.getpid()}] Cleaning Up Services", "Teardown database connections and services"), + (f"[Worker PID {os.getpid()}] Clearing Temporary Files", "Removing temporary directories and cache"), + (f"[Worker PID {os.getpid()}] Finalizing Shutdown", "Completing cleanup and logging"), + ] + else: + steps = [ + ("Stopping Server", "Gracefully stopping the web server"), + ("Cancelling Background Tasks", "Stopping file synchronization and background jobs"), + ("Cleaning Up Services", "Teardown database connections and services"), + ("Clearing Temporary Files", "Removing temporary directories and cache"), + ("Finalizing Shutdown", "Completing cleanup and logging"), + ] for title, description in steps: progress.add_step(title, description) diff --git a/src/backend/base/langflow/main.py b/src/backend/base/langflow/main.py index 7ce1346be..fdab17e67 100644 --- a/src/backend/base/langflow/main.py +++ b/src/backend/base/langflow/main.py @@ -191,10 +191,14 @@ def get_lifespan(*, fix_migration=False, version=None): finally: # Clean shutdown with progress indicator # Create shutdown progress (show verbose timing if log level is DEBUG) + from langflow.__main__ import get_number_of_workers from langflow.cli.progress import create_langflow_shutdown_progress log_level = os.getenv("LANGFLOW_LOG_LEVEL", "info").lower() - shutdown_progress = create_langflow_shutdown_progress(verbose=log_level == "debug") + num_workers = get_number_of_workers(get_settings_service().settings.workers) + shutdown_progress = create_langflow_shutdown_progress( + verbose=log_level == "debug", multiple_workers=num_workers > 1 + ) try: # Step 0: Stopping Server @@ -227,7 +231,6 @@ def get_lifespan(*, fix_migration=False, version=None): # Show completion summary and farewell shutdown_progress.print_shutdown_summary() - shutdown_progress.print_farewell_message() except (sqlalchemy.exc.OperationalError, sqlalchemy.exc.DBAPIError) as e: # Case where the database connection is closed during shutdown diff --git a/uv.lock b/uv.lock index b9d1e20e0..9a39196c4 100644 --- a/uv.lock +++ b/uv.lock @@ -4085,11 +4085,11 @@ wheels = [ [[package]] name = "junitparser" -version = "4.0.0" +version = "4.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6e/c7/c5741009c9956053f0bffdac6b25c5dd6e53f4c0c2ed4d58956ea0739d46/junitparser-4.0.0.tar.gz", hash = "sha256:e9d234356172750a8b49708500a84bd9fb47c779d629b124de89a3070b310320", size = 25598, upload-time = "2025-06-22T19:45:57.166Z" } +sdist = { url = "https://files.pythonhosted.org/packages/13/cc/b6ec3f011725b06053c1e849b0be623176951ea518429128c01b73d45b8a/junitparser-4.0.1.tar.gz", hash = "sha256:00be98abaddef8c135db6bda3a52beb801b9b0f698da8cb1572ac2091ceae668", size = 25588, upload-time = "2025-06-23T21:56:43.184Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b9/bd/a795422f9542204e0d32095cb3d2c7916d23043be2d67fa59ba8d69c7c87/junitparser-4.0.0-py3-none-any.whl", hash = "sha256:715b624d0bc40bfc4a31099aa24202bf8aab7933c65ad537e7e34cb9290cd51f", size = 14566, upload-time = "2025-06-22T19:45:55.961Z" }, + { url = "https://files.pythonhosted.org/packages/96/7f/7a2634e5bc4c4b4b02cbd3fdcd4d99763be04d4e71e2260d647f6146cfe4/junitparser-4.0.1-py3-none-any.whl", hash = "sha256:9c142c3d688c1674cd66905d96963b5e047f9fc079fe66d462b5cd12cf128d55", size = 14566, upload-time = "2025-06-23T21:56:42.004Z" }, ] [[package]]