feat: Sync flows from FS to DB if flow has fs_path (#7043)
* feat: Sync flows from FS to DB if flow has fs_path * Changes following review * Simplify flow_mtimes handling * Move sync_flows_from_fs to setup.py --------- Co-authored-by: Gabriel Luiz Freitas Almeida <gabriel@langflow.org>
This commit is contained in:
parent
e75a6dad06
commit
62884f6c8b
2 changed files with 45 additions and 2 deletions
|
|
@ -23,7 +23,7 @@ from emoji import demojize, purely_emoji
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from sqlalchemy.exc import NoResultFound
|
from sqlalchemy.exc import NoResultFound
|
||||||
from sqlalchemy.orm import selectinload
|
from sqlalchemy.orm import selectinload
|
||||||
from sqlmodel import select
|
from sqlmodel import col, select
|
||||||
from sqlmodel.ext.asyncio.session import AsyncSession
|
from sqlmodel.ext.asyncio.session import AsyncSession
|
||||||
|
|
||||||
from langflow.base.constants import FIELD_FORMAT_ATTRIBUTES, NODE_FORMAT_ATTRIBUTES, ORJSON_OPTIONS
|
from langflow.base.constants import FIELD_FORMAT_ATTRIBUTES, NODE_FORMAT_ATTRIBUTES, ORJSON_OPTIONS
|
||||||
|
|
@ -978,3 +978,36 @@ async def get_or_create_default_folder(session: AsyncSession, user_id: UUID) ->
|
||||||
msg = "Failed to get or create default folder"
|
msg = "Failed to get or create default folder"
|
||||||
raise ValueError(msg) from e
|
raise ValueError(msg) from e
|
||||||
return FolderRead.model_validate(folder_obj, from_attributes=True)
|
return FolderRead.model_validate(folder_obj, from_attributes=True)
|
||||||
|
|
||||||
|
|
||||||
|
async def sync_flows_from_fs():
|
||||||
|
flow_mtimes = {}
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
async with session_scope() as session:
|
||||||
|
stmt = select(Flow).where(col(Flow.fs_path).is_not(None))
|
||||||
|
flows = (await session.exec(stmt)).all()
|
||||||
|
for flow in flows:
|
||||||
|
mtime = flow_mtimes.setdefault(flow.id, 0)
|
||||||
|
path = anyio.Path(flow.fs_path)
|
||||||
|
try:
|
||||||
|
if await path.exists():
|
||||||
|
new_mtime = (await path.stat()).st_mtime
|
||||||
|
if new_mtime > mtime:
|
||||||
|
update_data = orjson.loads(await path.read_text(encoding="utf-8"))
|
||||||
|
try:
|
||||||
|
for field_name in ("name", "description", "data", "locked"):
|
||||||
|
if new_value := update_data.get(field_name):
|
||||||
|
setattr(flow, field_name, new_value)
|
||||||
|
if folder_id := update_data.get("folder_id"):
|
||||||
|
flow.folder_id = UUID(folder_id)
|
||||||
|
await session.commit()
|
||||||
|
await session.refresh(flow)
|
||||||
|
except Exception: # noqa: BLE001
|
||||||
|
logger.exception(f"Couldn't update flow {flow.id} in database from path {path}")
|
||||||
|
flow_mtimes[flow.id] = new_mtime
|
||||||
|
except Exception: # noqa: BLE001
|
||||||
|
logger.exception(f"Error while handling flow file {path}")
|
||||||
|
except Exception: # noqa: BLE001
|
||||||
|
logger.exception("Error while syncing flows from database")
|
||||||
|
await asyncio.sleep(10)
|
||||||
|
|
|
||||||
|
|
@ -29,12 +29,17 @@ from langflow.initial_setup.setup import (
|
||||||
initialize_super_user_if_needed,
|
initialize_super_user_if_needed,
|
||||||
load_bundles_from_urls,
|
load_bundles_from_urls,
|
||||||
load_flows_from_directory,
|
load_flows_from_directory,
|
||||||
|
sync_flows_from_fs,
|
||||||
)
|
)
|
||||||
from langflow.interface.components import get_and_cache_all_types_dict
|
from langflow.interface.components import get_and_cache_all_types_dict
|
||||||
from langflow.interface.utils import setup_llm_caching
|
from langflow.interface.utils import setup_llm_caching
|
||||||
from langflow.logging.logger import configure
|
from langflow.logging.logger import configure
|
||||||
from langflow.middleware import ContentSizeLimitMiddleware
|
from langflow.middleware import ContentSizeLimitMiddleware
|
||||||
from langflow.services.deps import get_queue_service, get_settings_service, get_telemetry_service
|
from langflow.services.deps import (
|
||||||
|
get_queue_service,
|
||||||
|
get_settings_service,
|
||||||
|
get_telemetry_service,
|
||||||
|
)
|
||||||
from langflow.services.utils import initialize_services, teardown_services
|
from langflow.services.utils import initialize_services, teardown_services
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
|
@ -118,6 +123,7 @@ def get_lifespan(*, fix_migration=False, version=None):
|
||||||
rprint("[bold green]Starting Langflow...[/bold green]")
|
rprint("[bold green]Starting Langflow...[/bold green]")
|
||||||
|
|
||||||
temp_dirs: list[TemporaryDirectory] = []
|
temp_dirs: list[TemporaryDirectory] = []
|
||||||
|
sync_flows_from_fs_task = None
|
||||||
try:
|
try:
|
||||||
await initialize_services(fix_migration=fix_migration)
|
await initialize_services(fix_migration=fix_migration)
|
||||||
setup_llm_caching()
|
setup_llm_caching()
|
||||||
|
|
@ -128,6 +134,7 @@ def get_lifespan(*, fix_migration=False, version=None):
|
||||||
await create_or_update_starter_projects(all_types_dict)
|
await create_or_update_starter_projects(all_types_dict)
|
||||||
telemetry_service.start()
|
telemetry_service.start()
|
||||||
await load_flows_from_directory()
|
await load_flows_from_directory()
|
||||||
|
sync_flows_from_fs_task = asyncio.create_task(sync_flows_from_fs())
|
||||||
queue_service = get_queue_service()
|
queue_service = get_queue_service()
|
||||||
if not queue_service.is_started(): # Start if not already started
|
if not queue_service.is_started(): # Start if not already started
|
||||||
queue_service.start()
|
queue_service.start()
|
||||||
|
|
@ -140,6 +147,9 @@ def get_lifespan(*, fix_migration=False, version=None):
|
||||||
finally:
|
finally:
|
||||||
# Clean shutdown
|
# Clean shutdown
|
||||||
logger.info("Cleaning up resources...")
|
logger.info("Cleaning up resources...")
|
||||||
|
if sync_flows_from_fs_task:
|
||||||
|
sync_flows_from_fs_task.cancel()
|
||||||
|
await asyncio.wait([sync_flows_from_fs_task])
|
||||||
await teardown_services()
|
await teardown_services()
|
||||||
await logger.complete()
|
await logger.complete()
|
||||||
temp_dir_cleanups = [asyncio.to_thread(temp_dir.cleanup) for temp_dir in temp_dirs]
|
temp_dir_cleanups = [asyncio.to_thread(temp_dir.cleanup) for temp_dir in temp_dirs]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue