ref: improve starter projects setup performance (#8549)
* Update starter project logic to only delete and recreate if necessary * move updates to correct location * [autofix.ci] apply automated fixes * remove unused var * Add to env var md * Use setting service for env vars * [autofix.ci] apply automated fixes --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
092c12f7b6
commit
14f3a2c111
3 changed files with 78 additions and 29 deletions
|
|
@ -223,6 +223,7 @@ The following table lists the environment variables supported by Langflow.
|
|||
| <Link id="LANGFLOW_SECRET_KEY"/><span class="env-prefix">LANGFLOW_</span>SECRET_KEY | String | Auto-generated | Key used for encrypting sensitive data like API keys. If a key is not provided, a secure key is auto-generated. For production environments with multiple instances, you should explicitly set this to ensure consistent encryption across instances. |
|
||||
| <Link id="LANGFLOW_STORE"/><span class="env-prefix">LANGFLOW_</span>STORE | Boolean | `true` | Enable the Langflow Store.<br/>See [`--store` option](./configuration-cli.md#run-store). |
|
||||
| <Link id="LANGFLOW_STORE_ENVIRONMENT_VARIABLES"/><span class="env-prefix">LANGFLOW_</span>STORE_ENVIRONMENT_VARIABLES | Boolean | `true` | Store environment variables as [global variables](../Configuration/configuration-global-variables.md) in the database. |
|
||||
| <Link id="LANGFLOW_CREATE_STARTER_PROJECTS"/><span class="env-prefix">LANGFLOW_</span>CREATE_STARTER_PROJECTS | Boolean | `true` | If this option is enabled, Langflow creates starter projects during initialization. Set to `false` to skip all starter project creation and updates. |
|
||||
| <Link id="LANGFLOW_UPDATE_STARTER_PROJECTS"/><span class="env-prefix">LANGFLOW_</span>UPDATE_STARTER_PROJECTS | Boolean | `true` | If this option is enabled, Langflow updates starter projects with the latest component versions when initializing. |
|
||||
| <Link id="LANGFLOW_SUPERUSER"/><span class="env-prefix">LANGFLOW_</span>SUPERUSER | String | `langflow` | Set the name for the superuser. Required if <span class="env-prefix">LANGFLOW_</span>AUTO_LOGIN is set to `false`.<br/>See [`superuser --username` option](./configuration-cli.md#superuser-username). |
|
||||
| <Link id="LANGFLOW_SUPERUSER_PASSWORD"/><span class="env-prefix">LANGFLOW_</span>SUPERUSER_PASSWORD | String | `langflow` | Set the password for the superuser. Required if <span class="env-prefix">LANGFLOW_</span>AUTO_LOGIN is set to `false`.<br/>See [`superuser --password` option](./configuration-cli.md#superuser-password). |
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import asyncio
|
|||
import copy
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import zipfile
|
||||
|
|
@ -690,7 +689,7 @@ async def get_all_flows_similar_to_project(session: AsyncSession, folder_id: UUI
|
|||
return list((await session.exec(stmt)).first().flows)
|
||||
|
||||
|
||||
async def delete_start_projects(session, folder_id) -> None:
|
||||
async def delete_starter_projects(session, folder_id) -> None:
|
||||
flows = await get_all_flows_similar_to_project(session, folder_id)
|
||||
for flow in flows:
|
||||
await session.delete(flow)
|
||||
|
|
@ -703,7 +702,7 @@ async def folder_exists(session, folder_name):
|
|||
return folder is not None
|
||||
|
||||
|
||||
async def create_starter_folder(session):
|
||||
async def get_or_create_starter_folder(session):
|
||||
if not await folder_exists(session, STARTER_FOLDER_NAME):
|
||||
new_folder = FolderCreate(name=STARTER_FOLDER_NAME, description=STARTER_FOLDER_DESCRIPTION)
|
||||
db_folder = Folder.model_validate(new_folder, from_attributes=True)
|
||||
|
|
@ -899,21 +898,31 @@ async def find_existing_flow(session, flow_id, flow_endpoint_name):
|
|||
return None
|
||||
|
||||
|
||||
async def create_or_update_starter_projects(all_types_dict: dict, *, do_create: bool = True) -> None:
|
||||
async def create_or_update_starter_projects(all_types_dict: dict) -> None:
|
||||
"""Create or update starter projects.
|
||||
|
||||
Args:
|
||||
all_types_dict (dict): Dictionary containing all component types and their templates
|
||||
do_create (bool, optional): Whether to create new projects. Defaults to True.
|
||||
"""
|
||||
successfully_created_projects = 0
|
||||
if not get_settings_service().settings.create_starter_projects:
|
||||
# no-op for environments that don't want to create starter projects.
|
||||
# note that this doesn't check if the starter projects are already loaded in the db;
|
||||
# this is intended to be used to skip all startup project logic.
|
||||
return
|
||||
|
||||
async with session_scope() as session:
|
||||
new_folder = await create_starter_folder(session)
|
||||
new_folder = await get_or_create_starter_folder(session)
|
||||
starter_projects = await load_starter_projects()
|
||||
await delete_start_projects(session, new_folder.id)
|
||||
await copy_profile_pictures()
|
||||
for project_path, project in starter_projects:
|
||||
try:
|
||||
|
||||
if get_settings_service().settings.update_starter_projects:
|
||||
logger.debug("Updating starter projects")
|
||||
# 1. Delete all existing starter projects
|
||||
successfully_updated_projects = 0
|
||||
await delete_starter_projects(session, new_folder.id)
|
||||
await copy_profile_pictures()
|
||||
|
||||
# 2. Update all starter projects with the latest component versions (this modifies the actual file data)
|
||||
for project_path, project in starter_projects:
|
||||
(
|
||||
project_name,
|
||||
project_description,
|
||||
|
|
@ -925,23 +934,16 @@ async def create_or_update_starter_projects(all_types_dict: dict, *, do_create:
|
|||
project_gradient,
|
||||
project_tags,
|
||||
) = get_project_data(project)
|
||||
do_update_starter_projects = (
|
||||
os.environ.get("LANGFLOW_UPDATE_STARTER_PROJECTS", "true").lower() == "true"
|
||||
updated_project_data = update_projects_components_with_latest_component_versions(
|
||||
project_data.copy(), all_types_dict
|
||||
)
|
||||
if do_update_starter_projects:
|
||||
updated_project_data = update_projects_components_with_latest_component_versions(
|
||||
project_data.copy(), all_types_dict
|
||||
)
|
||||
updated_project_data = update_edges_with_latest_component_versions(updated_project_data)
|
||||
if updated_project_data != project_data:
|
||||
project_data = updated_project_data
|
||||
# We also need to update the project data in the file
|
||||
await update_project_file(project_path, project, updated_project_data)
|
||||
if do_create and project_name and project_data:
|
||||
existing_flows = await get_all_flows_similar_to_project(session, new_folder.id)
|
||||
for existing_project in existing_flows:
|
||||
await session.delete(existing_project)
|
||||
updated_project_data = update_edges_with_latest_component_versions(updated_project_data)
|
||||
if updated_project_data != project_data:
|
||||
project_data = updated_project_data
|
||||
await update_project_file(project_path, project, updated_project_data)
|
||||
|
||||
try:
|
||||
# Create the updated starter project
|
||||
create_new_project(
|
||||
session=session,
|
||||
project_name=project_name,
|
||||
|
|
@ -955,10 +957,48 @@ async def create_or_update_starter_projects(all_types_dict: dict, *, do_create:
|
|||
project_tags=project_tags,
|
||||
new_folder_id=new_folder.id,
|
||||
)
|
||||
except Exception: # noqa: BLE001
|
||||
logger.exception(f"Error while creating starter project {project_name}")
|
||||
|
||||
successfully_updated_projects += 1
|
||||
logger.debug(f"Successfully updated {successfully_updated_projects} starter projects")
|
||||
else:
|
||||
# Even if we're not updating starter projects, we still need to create any that don't exist
|
||||
logger.debug("Creating new starter projects")
|
||||
successfully_created_projects = 0
|
||||
existing_flows = await get_all_flows_similar_to_project(session, new_folder.id)
|
||||
existing_flow_names = [existing_flow.name for existing_flow in existing_flows]
|
||||
for _, project in starter_projects:
|
||||
(
|
||||
project_name,
|
||||
project_description,
|
||||
project_is_component,
|
||||
updated_at_datetime,
|
||||
project_data,
|
||||
project_icon,
|
||||
project_icon_bg_color,
|
||||
project_gradient,
|
||||
project_tags,
|
||||
) = get_project_data(project)
|
||||
if project_name not in existing_flow_names:
|
||||
try:
|
||||
create_new_project(
|
||||
session=session,
|
||||
project_name=project_name,
|
||||
project_description=project_description,
|
||||
project_is_component=project_is_component,
|
||||
updated_at_datetime=updated_at_datetime,
|
||||
project_data=project_data,
|
||||
project_icon=project_icon,
|
||||
project_icon_bg_color=project_icon_bg_color,
|
||||
project_gradient=project_gradient,
|
||||
project_tags=project_tags,
|
||||
new_folder_id=new_folder.id,
|
||||
)
|
||||
except Exception: # noqa: BLE001
|
||||
logger.exception(f"Error while creating starter project {project_name}")
|
||||
successfully_created_projects += 1
|
||||
except Exception: # noqa: BLE001
|
||||
logger.exception(f"Error while creating starter project {project_name}")
|
||||
logger.debug(f"Successfully created {successfully_created_projects} starter projects")
|
||||
logger.debug(f"Successfully created {successfully_created_projects} starter projects")
|
||||
|
||||
|
||||
async def initialize_super_user_if_needed() -> None:
|
||||
|
|
|
|||
|
|
@ -256,6 +256,14 @@ class Settings(BaseSettings):
|
|||
"""If set to True, Langflow will only partially load components at startup and fully load them on demand.
|
||||
This significantly reduces startup time but may cause a slight delay when a component is first used."""
|
||||
|
||||
# Starter Projects
|
||||
create_starter_projects: bool = True
|
||||
"""If set to True, Langflow will create starter projects. If False, skips all starter project setup.
|
||||
Note that this doesn't check if the starter projects are already loaded in the db;
|
||||
this is intended to be used to skip all startup project logic."""
|
||||
update_starter_projects: bool = True
|
||||
"""If set to True, Langflow will update starter projects."""
|
||||
|
||||
@field_validator("event_delivery", mode="before")
|
||||
@classmethod
|
||||
def set_event_delivery(cls, value, info):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue