CodeVetter · Repo Unpack · Behavior traces
langflow-ai/langflow
Traced end-to-end: (1) `langflow run` startup from console script through lifespan service init; (2) a synchronous POST /api/v1/run/{flow} execution; (3) the editor's build+events flow; (4) flow deletion cascade; (5) v2 workflow dispatch; (6) shutdown ordering. Each hop below names the actual functions in call order.
- Startup trace: `langflow` script -> langflow_launcher.main (Darwin re-exec for OBJC fork safety) -> typer `run` command -> env-file load -> production preflight + Postgres version check -> ProcessManager spawns gunicorn (Linux) or uvicorn (win/mac, workers clamped to 1) -> create_app() -> get_lifespan().
- Lifespan init order: configure() logging -> optional sentry_sdk.init -> ensure_production_preflight -> initialize_services (register_all_service_factories -> initialize_database + alembic -> hydrate_policy_bundle -> hydrate_catalog_policy -> setup_superuser -> clean_transactions/vertex_builds/authz_audit_log) -> initialize_environment_variables -> telemetry writer + audit_cleanup + model_provider_policy_refresh workers -> setup_llm_caching -> profile pictures -> KB/memory-base backfills -> Prometheus -> load_bundles_from_urls -> get_and_cache_all_types_dict -> create_or_update_starter_projects (FileLock) -> agentic globals -> telemetry_service.start -> MCPComposerService.start -> MCP server backfill -> load_flows_from_directory -> warm registry task -> sync_flows_from_fs + queue_service.start -> orphan sweep -> delayed init_mcp_servers -> streamable-HTTP managers -> lag monitor + DB pool instrumentation -> enterprise hooks.
- Superuser bootstrap: setup_superuser branches on AUTO_LOGIN — when on, it ignores a legacy default password, generates token_urlsafe(32), takes a FileLock (langflow_auto_login_superuser.lock), creates the user via auth.create_super_user, initializes variables and the default folder; when off, it requires configured credentials and deletes an unused default superuser on teardown.
- Sync run trace: POST /api/v1/run/{flow_id_or_name} -> api_key_security / get_current_user -> get_flow_by_id_or_endpoint_name (widen_for_shares) -> ensure_flow_permission(EXECUTE) -> _run_flow_internal -> _enforce_owner_only_tweaks -> resolve_serving_scope -> simple_run_flow -> set_current_model_provider_policy_context -> prepare_flow_build_for_user -> try_warm_run_graph or (process_tweaks -> raise_if_hitl_unsupported -> apply_global_variable_defaults -> Graph.from_payload) -> graph.set_run_id -> JobService.create_job + execute_with_status -> run_graph_internal -> graph.arun -> RunResponse -> _v1_run_response (content_blocks projection).
- Streaming run trace: _run_flow_internal(stream=True) creates an asyncio.Queue + client_consumed_queue, wraps them in a tokens EventManager, spawns run_flow_generator as a task (simple_run_flow(stream=True) -> event_manager.on_end/on_error -> sentinel None), and returns StreamingResponse(consume_and_yield(...)) with an on_disconnect cancel.
- Editor build trace: POST /api/v1/build/{flow_id}/flow -> session_scope _read_flow (owner-scoped, PUBLIC fallback) -> ensure_flow_permission(EXECUTE) with deny->404 reframe unless READ passes -> resolve_flow_data_override drops caller data unless flow:write -> prepare_flow_build_for_user sanitizes stored/inline data -> start_flow_build -> validate_targeted_inputs -> queue_service.create_queue(job_id) -> generate_flow_events task (build_graph_from_db/build_graph_from_data -> graph.prepare -> per-vertex build emits VertexBuildResponse events) -> returns {job_id} -> client polls GET /build/{job_id}/events -> _register_job_owner_or_cancel records ownership for cancel.
- Flow deletion trace: DELETE /api/v1/flows/{flow_id} -> owner/authz check -> cascade_delete_flow -> check_flow_has_deployed_versions guard -> delete messages -> transactions -> vertex_builds -> flow_versions -> spans then traces (FK order) -> authz_share rows -> flow row; rowcount==1 verifies removal.
- v2 workflow trace: POST /api/v2/workflows -> lfx create_workflow_router -> LangflowWorkflowHost.resolve_caller (oauth2_login cookie -> api_key query -> header -> get_current_user_for_workflow, in its own session_scope to avoid SQLite contention) -> get_flow (resolve_flow_for_execution) -> authorize (authorize_flow_action, deny->404) -> branch: run_sync_with_mapping (job-tracked), build_stream_response (v1 build-vertex loop + AG-UI), or submit_background_with_mapping (durable).
- Service resolution trace: get_service(ServiceType.X) -> ServiceManager.get (keyed lock) -> first-call discover_plugins (entry points `lfx.services` override=False, then lfx.toml/pyproject `[tool.lfx.services]` override=True) -> service_classes direct registration wins -> _create_service_from_class inspects __init__ params and recursively resolves ServiceType dependencies -> otherwise _create_service_from_factory resolves factory.dependencies then factory.create() + set_ready().
- Shutdown trace: lifespan finally -> cleanup_mcp_sessions FIRST (kill MCP subprocesses) -> enterprise shutdown hooks -> stop_event_loop_lag_monitor -> staged progress (stop server -> cancel sync_flows_from_fs/mcp_init/models_dev/warm_registry tasks + stop streamable-HTTP/project/agentic MCP managers + A2A push client + audit/policy workers) -> drain_pending_audit_writes -> teardown_services (30s cap; teardown_superuser deletes unused default superuser when AUTO_LOGIN off) -> shutdown_sandbox (QEMU guests) -> temp_dir cleanup -> summary.