Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: improve CLI parameter handling and cleanup unused code #4002

Merged
merged 10 commits into from
Oct 3, 2024
6 changes: 3 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -243,15 +243,15 @@ 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) \
--env-file $(env) \
--no-open-browser
else
@make install_backend && uv run langflow run \
--path $(path) \
--frontend-path $(path) \
--log-level $(log_level) \
--host $(host) \
--port $(port) \
Expand All @@ -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
Expand Down
127 changes: 71 additions & 56 deletions src/backend/base/langflow/__main__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import inspect
import platform
import socket
import sys
Expand Down Expand Up @@ -28,7 +29,6 @@
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

Expand Down Expand Up @@ -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),
Copy link
Collaborator

@cbornet cbornet Oct 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not related to this PR but it would probably be nice to use the syntax host: Annotated[str | None, typer.Option(None, help="Host to bind the server to.", show_default=False)] = None` for all the params for better type checking.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried applying the suggestion, but the code started breaking. I'll take a closer look at it later.

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"),
env_file: Path | None = typer.Option(
None,
help="Path to the .env file containing environment variables.",
show_default=False,
),
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(
envvar="LANGFLOW_LANGCHAIN_CACHE",
None,
help="Type of cache to use. (InMemoryCache, SQLiteCache)",
default=None,
show_default=False,
),
dev: bool = typer.Option(False, help="Run in development mode (may contain bugs)"),
path: str = typer.Option(
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,
),
):
"""
Expand All @@ -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
Expand Down Expand Up @@ -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.
Expand Down
14 changes: 14 additions & 0 deletions src/backend/base/langflow/services/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading