mirror of
https://github.com/hanzoai/flow.git
synced 2026-10-17 01:29:18 +00:00
Docs corpus (Langflow->Hanzo Flow, upstream URLs langflow-ai/langflow->hanzoai/ flow, docs.langflow.org->docs.hanzo.ai), starter-project template help-URLs, CI workflows, and test files. Remaining 'langflow' repo-wide is now dominated by LANGFLOW_* env-var names + langflow_ settings fields — the config-boundary layer deliberately preserved pending a coordinated FLOW_/HANZO_ alias-with-fallback (renaming them blind breaks every deployment).
5.5 KiB
5.5 KiB
Architecture Boundaries
Hanzo Flow 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.
Package dependency graph (one-way only)
frontend (TS) ──HTTP──▶ flow (api routers, integrations, distribution)
│
▼ may import
flow-base (services, graph, db, alembic)
│
▼ may import
lfx (executor core, base primitives, components)
│
▼ may import
langchain-core, pydantic, third-party SDKs
Dependency rules
lfxMUST NOT importflow.*orflow-base.*. If lfx code needs a service (auth, db, flow lookup), define an interface insidelfxand inject the implementation fromflow. The repo currently has ~13 upwardfrom flow.*imports insidesrc/lfx/src/lfx/components/...(auth, db, helpers) — these are known violations. Do not add more; prefer fixing them.flow-baseMAY importlfx. It MUST NOT import vendor-specific component modules fromflow.components.<vendor>.frontendtalks toflowonly via HTTP/WebSocket. No shared filesystem state.
"Where does this code go?" decision tree
Walk top-down. Stop at the first match.
- Is it framework-agnostic flow execution, base component classes, or
Componentprimitives? →src/lfx/src/lfx/(base/for shared primitives,components/for built-ins shipped with lfx). - Is it a FastAPI route, auth, db model, alembic migration, or a lifecycle-managed singleton?
→
src/backend/base/flow/(api/,services/X/,alembic/versions/). - Is it a vendor integration (OpenAI, Pinecone, Notion, …) — a
Componentsubclass that wraps a third-party SDK? →src/lfx/src/lfx/components/<category>/and update its__init__.pyalphabetically. Never rename the class. - Is it UI, state, or icons?
→
src/frontend/src/. If it consumes a new API field, also updatesrc/frontend/src/types/. - Is it CLI behavior for
lfx run/lfx serve? →src/lfx/src/lfx/cli/. - Is it a SQLAlchemy/SQLModel model change?
→
services/database/models/ANDmake alembic-revision message="..."AND apply withmake alembic-upgrade. - Is it a flow JSON schema change? → STOP. Existing saved flows must keep loading. Add a version mapping; do not mutate the existing shape. See CONTRACTS.md.
- Is it shared by both
lfxandflow-base? →src/lfx/src/lfx/base/, neverflow/base/.
Dependency direction — bad/good examples
- Bad:
from flow.services.deps import session_scopeinsidesrc/lfx/.... Good: Definelfx.interfaces.SessionProvider, accept it as a constructor arg;flowwires the concretesession_scopeat startup. - Bad:
from flow.components.openai import ...insideflow-basecore (api/,services/,graph/). Good: Components are loaded dynamically via the component registry; core code referencesComponentonly. - Bad: A new
MyHelperServicethat's just functions. Good: Utility functions go inflow/helpers/orlfx/utils/. A service inherits fromservices/base.Serviceand is registered viaservices/factory.py.
API change protocol
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 inapi/router.py. v2 is not "future"; the old cursor rule was wrong.- Add a new endpoint to v2 only if it (a) replaces a v1 endpoint with a breaking shape change, or (b) belongs to one of the four v2 domains. Otherwise extend v1 additively.
- A breaking change to a v1 endpoint is forbidden. Add a v2 sibling and leave v1 in place.
Cross-cutting change protocol
A change that touches a request/response shape MUST update three places in the same PR:
- The pydantic model in
flow/api/v{1,2}/schemas.py(or the route's local schema). - The TypeScript type in
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. - If the field is persisted: a new alembic revision (
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.
Service vs utility vs component
- Service (
services/<name>/): lifecycle-managed singleton, inheritsservices.base.Service, registered throughservices/factory.py, accessed viaservices/deps.py. Use when the thing has state, startup/shutdown, or shared connections (db, cache, queue). - Utility (
helpers/,utils/, orlfx/utils/): pure or near-pure functions. Use when there is no shared state and no lifecycle. - Component (
src/lfx/src/lfx/components/<category>/): user-visible node in the graph, subclass ofComponent, withdisplay_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 flow/base/
Both exist. Both have agents/, data/, models/, prompts/. New shared primitives go in src/lfx/src/lfx/base/. The flow/base/ tree is legacy; do not add to it.