docs/agents/ARCHITECTURE.md
Langflow is a runnable base application, a curated distribution, an executor SDK, standalone extensions, and one frontend. Dependencies point in one direction. Most "off-narrative" code is a boundary violation. Read this before adding a new file.
frontend (TS) ──HTTP──▶ langflow-base (UI/API, services, graph, db, alembic)
│
▼ may import
lfx (executor, primitives, built-in components)
│
▼ may import
langchain-core, pydantic
langflow (curated distribution) ──depends on──▶ langflow-base
│
└──depends on──▶ standalone lfx-* extensions ──depends on──▶ lfx
lfx MUST NOT import langflow.*. (langflow-base installs the
langflow import package.) If LFX code needs a service, define an interface
inside lfx and inject the implementation from the application. Existing
upward imports are known violations; do not add more.langflow-base MAY import lfx. It MUST NOT import vendor-specific component modules from langflow.components.<vendor>.lfx-* extensions MAY import the public LFX bundle API. They
MUST NOT import application services from langflow-base.langflow is dependency metadata, not another application layer. It adds
the curated extension set to the same langflow-base executable and runtime.frontend talks to langflow only via HTTP/WebSocket. No shared filesystem state.Walk top-down. Stop at the first match.
Component primitives?
→ src/lfx/src/lfx/ (base/ for shared primitives, components/ for built-ins shipped with lfx).src/backend/base/langflow/ (api/, services/X/, alembic/versions/).Component subclass that wraps a third-party SDK?
→ a standalone package under src/bundles/<provider>/, with an extension.json manifest. Add it to the curated langflow dependencies only when it belongs in the default distribution. Never rename the component class.src/frontend/src/. If it consumes a new API field, also update src/frontend/src/types/.lfx run / lfx serve?
→ src/lfx/src/lfx/cli/.services/database/models/ AND make alembic-revision message="..." AND apply with make alembic-upgrade.lfx and langflow-base?
→ src/lfx/src/lfx/base/, never langflow/base/.from langflow.services.deps import session_scope inside src/lfx/....
Good: Define lfx.interfaces.SessionProvider, accept it as a constructor arg; langflow wires the concrete session_scope at startup.from langflow.components.openai import ... inside langflow-base core (api/, services/, graph/).
Good: Components are loaded dynamically via the component registry; core code references Component only.MyHelperService that's just functions.
Good: Utility functions go in langflow/helpers/ or lfx/utils/. A service inherits from services/base.Service and is registered via services/factory.py.api/v1/ is the live, stable surface (~25 routers). Existing v1 endpoints MUST stay backwards-compatible: only additive fields, never rename or remove.api/v2/ is the active redesign surface (files, mcp, registration, workflow) — both are mounted at runtime in api/router.py. v2 is not "future"; the old cursor rule was wrong.A change that touches a request/response shape MUST update three places in the same PR:
langflow/api/v{1,2}/schemas.py (or the route's local schema).src/frontend/src/types/ consumed by the affected page/store. There is no OpenAPI generator — the types are hand-maintained, so the frontend silently breaks at runtime if you skip this.make alembic-revision message=...) AND a flow-JSON version mapping if the shape lives inside saved flows.If you cannot do all three in one PR, do not start.
services/<name>/): lifecycle-managed singleton, inherits services.base.Service, registered through services/factory.py, accessed via services/deps.py. Use when the thing has state, startup/shutdown, or shared connections (db, cache, queue).helpers/, utils/, or lfx/utils/): pure or near-pure functions. Use when there is no shared state and no lifecycle.src/lfx/src/lfx/components/<category>/): user-visible node in the graph, subclass of Component, with display_name, inputs, outputs. Use only when the user must wire it on the canvas. Do not add a Component to expose internal plumbing.lfx/base/ vs langflow/base/Both exist. Both have agents/, data/, models/, prompts/. New shared primitives go in src/lfx/src/lfx/base/. The langflow/base/ tree is legacy; do not add to it.