docs/agents/ARCHITECTURE.md
Langflow is three Python packages and one frontend, layered with one-way dependencies. Most "off-narrative" code is a boundary violation. Read this before adding a new file.
frontend (TS) ──HTTP──▶ langflow (api routers, integrations, distribution)
│
▼ may import
langflow-base (services, graph, db, alembic)
│
▼ may import
lfx (executor core, base primitives, components)
│
▼ may import
langchain-core, pydantic, third-party SDKs
lfx MUST NOT import langflow.* or langflow-base.*. If lfx code needs a service (auth, db, flow lookup), define an interface inside lfx and inject the implementation from langflow. The repo currently has ~13 upward from langflow.* imports inside src/lfx/src/lfx/components/... (auth, db, helpers) — these are known violations. Do not add more; prefer fixing them.langflow-base MAY import lfx. It MUST NOT import vendor-specific component modules from langflow.components.<vendor>.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?
→ src/lfx/src/lfx/components/<category>/ and update its __init__.py alphabetically. Never rename the 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.