langflow-ai/langflow
Langflow ships as a uv workspace whose public dependency direction is langflow -> langflow-base -> lfx, plus ~24 lfx-* provider bundle packages and a React/Vite frontend. The primary entrypoint is the `langflow` console script (langflow_launcher -> Typer app in __main__.py -> `langflow run`), which boots a FastAPI app created by create_app() in main.py and served by gunicorn on Linux or direct uvicorn on Windows/macOS. All cross-cutting capabilities (auth, DB, cache, storage, telemetry, tracing, job queue, ...) live behind a singleton ServiceManager in lfx with factory registration plus entry-point/config-file plugin discovery. Persistence is SQLAlchemy 2 async + SQLModel (aiosqlite default, Postgres supported) migrated by Alembic. The graph execution engine (Graph, Vertex, Edge) lives in lfx and is shared by the server and the standalone `lfx run`/`lfx serve` CLIs.
- The `langflow` console script resolves to langflow.langflow_launcher:main; on Darwin it re-execs `python -m langflow.__main__` after setting OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES because Objective-C fork-safety would otherwise SIGSEGV gunicorn workers.
- `langflow.__main__` is a Typer app exposing commands run, superuser, api_key, copy_db, migration, migrate-mcp, reconcile-kb-from-disk; it mounts an `lfx` sub-app (serve/run) and an `observability` group from lfx.cli.
- create_app() in main.py builds the FastAPI app: ContentSizeLimit + Sentry + CORS middleware, JavaScriptMIMEType fix, an execution-client header binder, a multipart-boundary validator for /api/v1/files/upload, X-Forwarded-Prefix handling, comma-flattened query strings, and Accept-Language locale resolution; it mounts router (/api/v1, /api/v2), health_check_router, log_router, then plugin routes.
- The uv workspace members are src/backend/base (langflow-base), the root (langflow), src/lfx, src/langflow-stepflow, src/sdk, and ~24 src/bundles/* packages; the curated distribution declares bounded ranges for lfx-* bundles (e.g. lfx-openai>=0.1.4,<1.0.0) while lfx-bundles stays opt-in.
- The documented package layering is frontend ->HTTP-> langflow-base -> lfx -> langchain-core/pydantic, with lfx forbidden from importing langflow.* and vendor components living only in src/bundles/<provider> packages.
- All services hang off a singleton ServiceManager (lfx/services/manager.py) created lazily via get_service_manager(); register_all_service_factories() in langflow/services/utils.py registers ~25 factories (settings, cache, chat, database, session, storage, variable, telemetry, tracing, transaction, state, checkpoint, job_queue, task, store, shared_component_cache, auth, authorization, policy_bundle, catalog_policy, model_provider_policy, mcp_composer, executor).
- langflow/services/deps.py exposes typed getters (get_db_service, get_auth_service, get_queue_service, ...) and the canonical async session_scope() context manager; get_session is deprecated and raises NotImplementedError. get_auth_service() defensively swaps out lfx's no-op AuthService stub for Langflow's JWT implementation.
- Persistence is SQLModel/SQLAlchemy async (aiosqlite default via LANGFLOW_DATABASE_URL=sqlite:///./langflow.db, Postgres via postgresql://) with Alembic migrations in langflow/alembic; a Postgres advisory lock (0x4C616E67666C6F77) serializes concurrent `alembic upgrade` across workers.
- The execution engine is lfx.graph.graph.base.Graph (~3.2k lines) with from_payload(), arun(), async_start(), build_vertex(), prepare(), RunnableVerticesManager, checkpoint support (GraphCheckpoint), OTel `flow.execute` spans, and per-run state (run_id, session_id, end_user_id, persist_messages).
- The frontend is React 19 + TypeScript + Vite + Zustand stores (flowStore, authStore, ...) + @xyflow/react canvas, packaged as `langflow` v1.12.2 in src/frontend/package.json; the built SPA is served by the backend via setup_static_files() which reserves /api GET/HEAD to return JSON 404s instead of the SPA shell.
- Deployment shape: `langflow run` uses gunicorn multi-worker fork on Linux and direct uvicorn (clamped to 1 worker) on Windows/macOS; ensure_multi_worker_safe() refuses to boot with >1 worker on the default in-memory job queue unless LANGFLOW_JOB_QUEUE_TYPE=redis or LANGFLOW_DANGEROUSLY_ALLOW_MULTI_WORKER_WITHOUT_SHARED_QUEUE=true.
- Observability is wired through lfx.observability: instrument_fastapi_app(app), execution_client/execution_protocol contextvars, an event-loop lag monitor, DB pool metrics, a Prometheus HTTP server gated by LANGFLOW_PROMETHEUS_PORT, and optional sentry_sdk init from LANGFLOW_SENTRY_DSN inside the lifespan.
- Build/dev commands are Makefile-driven: `make init` (uv sync --frozen --extra postgresql + npm + pre-commit), `make backend`/`make frontend` dev servers, `make unit_tests`, `make lint` (mypy); Python tooling uses ruff with select=ALL at line-length 120 and a large per-file-ignores table codifying intentional violations.