From ed7ec6a332c755b54cf266a188a153b19ba053b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8Dtalo=20Johnny?= Date: Thu, 3 Oct 2024 13:10:04 -0300 Subject: [PATCH] feat: improve CLI parameter handling and cleanup unused code (#4002) * chore: remove envvar parameter from CLI option * chore: add show_default=False flag to CLI option * chore: update argument type to accept None * feat: add missing Settings to CLI options * feat: add default path value for components * chore: rename CLI parameters to match Settings * feat: update function to enforce parameter hierarchy CLI > specific env_file > default env_file * fix: adjust CLI parameter code to pass unit tests * fix: mypy error arg-type * fix: little ajustments --- Makefile | 6 +- src/backend/base/langflow/__main__.py | 131 ++++++++++-------- .../base/langflow/services/settings/base.py | 14 ++ 3 files changed, 90 insertions(+), 61 deletions(-) diff --git a/Makefile b/Makefile index 6fa3b6293..c28b7123f 100644 --- a/Makefile +++ b/Makefile @@ -243,7 +243,7 @@ start: ifeq ($(open_browser),false) @make install_backend && uv run langflow run \ - --path $(path) \ + --frontend-path $(path) \ --log-level $(log_level) \ --host $(host) \ --port $(port) \ @@ -251,7 +251,7 @@ ifeq ($(open_browser),false) --no-open-browser else @make install_backend && uv run langflow run \ - --path $(path) \ + --frontend-path $(path) \ --log-level $(log_level) \ --host $(host) \ --port $(port) \ @@ -262,7 +262,7 @@ setup_devcontainer: ## set up the development container make install_backend make install_frontend make build_frontend - uv run langflow --path src/frontend/build + uv run langflow --frontend-path src/frontend/build setup_env: ## set up the environment @sh ./scripts/setup/setup_env.sh diff --git a/src/backend/base/langflow/__main__.py b/src/backend/base/langflow/__main__.py index 3a77fd82b..f972fab6c 100644 --- a/src/backend/base/langflow/__main__.py +++ b/src/backend/base/langflow/__main__.py @@ -1,3 +1,4 @@ +import inspect import platform import socket import sys @@ -28,7 +29,6 @@ from langflow.services.database.utils import session_getter from langflow.services.deps import get_db_service, get_settings_service, session_scope from langflow.services.settings.constants import DEFAULT_SUPERUSER from langflow.services.utils import initialize_services -from langflow.utils.util import update_settings from langflow.utils.version import fetch_latest_version, get_version_info from langflow.utils.version import is_pre_release as langflow_is_pre_release @@ -79,69 +79,73 @@ def set_var_for_macos_issue(): @app.command() def run( - host: str = typer.Option("127.0.0.1", help="Host to bind the server to.", envvar="LANGFLOW_HOST"), - workers: int = typer.Option(1, help="Number of worker processes.", envvar="LANGFLOW_WORKERS"), - timeout: int = typer.Option(300, help="Worker timeout in seconds.", envvar="LANGFLOW_WORKER_TIMEOUT"), - port: int = typer.Option(7860, help="Port to listen on.", envvar="LANGFLOW_PORT"), + host: str | None = typer.Option(None, help="Host to bind the server to.", show_default=False), + workers: int | None = typer.Option(None, help="Number of worker processes.", show_default=False), + worker_timeout: int | None = typer.Option(None, help="Worker timeout in seconds.", show_default=False), + port: int | None = typer.Option(None, help="Port to listen on.", show_default=False), components_path: Path | None = typer.Option( Path(__file__).parent / "components", help="Path to the directory containing custom components.", - envvar="LANGFLOW_COMPONENTS_PATH", + show_default=False, ), # .env file param - env_file: Path = typer.Option(None, help="Path to the .env file containing environment variables."), - log_level: str = typer.Option("critical", help="Logging level.", envvar="LANGFLOW_LOG_LEVEL"), - log_file: Path = typer.Option("logs/langflow.log", help="Path to the log file.", envvar="LANGFLOW_LOG_FILE"), - cache: str | None = typer.Option( - envvar="LANGFLOW_LANGCHAIN_CACHE", - help="Type of cache to use. (InMemoryCache, SQLiteCache)", - default=None, + env_file: Path | None = typer.Option( + None, + help="Path to the .env file containing environment variables.", + show_default=False, ), - dev: bool = typer.Option(False, help="Run in development mode (may contain bugs)"), - path: str = typer.Option( + log_level: str | None = typer.Option(None, help="Logging level.", show_default=False), + log_file: Path | None = typer.Option(None, help="Path to the log file.", show_default=False), + cache: str | None = typer.Option( + None, + help="Type of cache to use. (InMemoryCache, SQLiteCache)", + show_default=False, + ), + dev: bool | None = typer.Option(None, help="Run in development mode (may contain bugs)", show_default=False), + frontend_path: str | None = typer.Option( None, help="Path to the frontend directory containing build files. This is for development purposes only.", - envvar="LANGFLOW_FRONTEND_PATH", + show_default=False, ), - open_browser: bool = typer.Option( - True, + open_browser: bool | None = typer.Option( + None, help="Open the browser after starting the server.", - envvar="LANGFLOW_OPEN_BROWSER", + show_default=False, ), - remove_api_keys: bool = typer.Option( - False, + remove_api_keys: bool | None = typer.Option( + None, help="Remove API keys from the projects saved in the database.", - envvar="LANGFLOW_REMOVE_API_KEYS", + show_default=False, ), - backend_only: bool = typer.Option( - False, + backend_only: bool | None = typer.Option( + None, help="Run only the backend server without the frontend.", - envvar="LANGFLOW_BACKEND_ONLY", + show_default=False, ), - store: bool = typer.Option( - True, + store: bool | None = typer.Option( + None, help="Enables the store features.", - envvar="LANGFLOW_STORE", + show_default=False, ), - auto_saving: bool = typer.Option( - True, + auto_saving: bool | None = typer.Option( + None, help="Defines if the auto save is enabled.", - envvar="LANGFLOW_AUTO_SAVING", + show_default=False, ), - auto_saving_interval: int = typer.Option( - 1000, + auto_saving_interval: int | None = typer.Option( + None, help="Defines the debounce time for the auto save.", - envvar="LANGFLOW_AUTO_SAVING_INTERVAL", + show_default=False, ), - health_check_max_retries: bool = typer.Option( - True, + health_check_max_retries: bool | None = typer.Option( + None, help="Defines the number of retries for the health check.", - envvar="LANGFLOW_HEALTH_CHECK_MAX_RETRIES", + show_default=False, ), - max_file_size_upload: int = typer.Option( - 100, + max_file_size_upload: int | None = typer.Option( + None, help="Defines the maximum file size for the upload in MB.", - envvar="LANGFLOW_MAX_FILE_SIZE_UPLOAD", + show_default=False, ), ): """ @@ -153,33 +157,44 @@ def run( if env_file: load_dotenv(env_file, override=True) + logger.debug(f"Loading config from file: '{env_file}'") - update_settings( - dev=dev, - remove_api_keys=remove_api_keys, - cache=cache, - components_path=components_path, - store=store, - auto_saving=auto_saving, - auto_saving_interval=auto_saving_interval, - health_check_max_retries=health_check_max_retries, - max_file_size_upload=max_file_size_upload, - ) - # create path object if path is provided - static_files_dir: Path | None = Path(path) if path else None settings_service = get_settings_service() - settings_service.set("backend_only", backend_only) + + frame = inspect.currentframe() + valid_args: list = [] + values: dict = {} + if frame is not None: + arguments, _, _, values = inspect.getargvalues(frame) + valid_args = [arg for arg in arguments if values[arg] is not None] + + for arg in valid_args: + if arg == "components_path": + settings_service.settings.update_settings(components_path=components_path) + elif hasattr(settings_service.settings, arg): + settings_service.set(arg, values[arg]) + logger.debug(f"Loading config from cli parameter '{arg}': '{values[arg]}'") + + host = settings_service.settings.host + port = settings_service.settings.port + workers = settings_service.settings.workers + worker_timeout = settings_service.settings.worker_timeout + log_level = settings_service.settings.log_level + frontend_path = settings_service.settings.frontend_path + backend_only = settings_service.settings.backend_only + + # create path object if frontend_path is provided + static_files_dir: Path | None = Path(frontend_path) if frontend_path else None + app = setup_app(static_files_dir=static_files_dir, backend_only=backend_only) # check if port is being used if is_port_in_use(port, host): port = get_free_port(port) - settings_service.set("worker_timeout", timeout) - options = { "bind": f"{host}:{port}", "workers": get_number_of_workers(workers), - "timeout": timeout, + "timeout": worker_timeout, } # Define an env variable to know if we are just testing the server @@ -472,7 +487,7 @@ def migration( @app.command() def api_key( - log_level: str = typer.Option("error", help="Logging level.", envvar="LANGFLOW_LOG_LEVEL"), + log_level: str = typer.Option("error", help="Logging level."), ): """ Creates an API key for the default superuser if AUTO_LOGIN is enabled. diff --git a/src/backend/base/langflow/services/settings/base.py b/src/backend/base/langflow/services/settings/base.py index 8aa9b8ff4..74ce6a227 100644 --- a/src/backend/base/langflow/services/settings/base.py +++ b/src/backend/base/langflow/services/settings/base.py @@ -146,6 +146,20 @@ class Settings(BaseSettings): """If set to True, Langflow will keep track of each vertex builds (outputs) in the UI for any flow.""" # Config + host: str = "127.0.0.1" + """The host on which Langflow will run.""" + port: int = 7860 + """The port on which Langflow will run.""" + workers: int = 1 + """The number of workers to run.""" + log_level: str = "critical" + """The log level for Langflow.""" + log_file: str | None = "logs/langflow.log" + """The path to log file for Langflow.""" + frontend_path: str | None = None + """The path to the frontend directory containing build files. This is for development purposes only..""" + open_browser: bool = False + """If set to True, Langflow will open the browser on startup.""" auto_saving: bool = True """If set to True, Langflow will auto save flows.""" auto_saving_interval: int = 1000