feat: canonical flow directory with sync engine (files are source of truth)
This commit is contained in:
parent
7d5ade6b6a
commit
c65e3ed332
16 changed files with 4167 additions and 42 deletions
|
|
@ -56,7 +56,82 @@ def list_definitions(session: Session) -> list[FlowDefinition]:
|
|||
return list(session.exec(select(FlowDefinition).order_by(FlowDefinition.name)).all())
|
||||
|
||||
|
||||
def _validate_flow_json(flow_json: dict) -> None:
|
||||
def get_definition_by_source_path(
|
||||
session: Session, source_path: str
|
||||
) -> FlowDefinition | None:
|
||||
"""Find the definition synced from a given backend/flows filename."""
|
||||
return session.exec(
|
||||
select(FlowDefinition).where(FlowDefinition.source_path == source_path)
|
||||
).first()
|
||||
|
||||
|
||||
def set_definition_name(
|
||||
session: Session, definition: FlowDefinition, name: str
|
||||
) -> FlowDefinition:
|
||||
"""Rename a definition and record its source file. Name clashes 409."""
|
||||
definition.name = name
|
||||
session.add(definition)
|
||||
try:
|
||||
session.commit()
|
||||
except IntegrityError as exc:
|
||||
session.rollback()
|
||||
raise ConflictError(f"flow {name!r} already exists") from exc
|
||||
session.refresh(definition)
|
||||
return definition
|
||||
|
||||
|
||||
def next_version_no(session: Session, flow_id: UUID) -> int:
|
||||
max_no = session.exec(
|
||||
select(func.max(FlowVersion.version_no)).where(FlowVersion.flow_id == flow_id)
|
||||
).one()
|
||||
return (max_no or 0) + 1
|
||||
|
||||
|
||||
def delete_definition_cascade(
|
||||
session: Session, definition: FlowDefinition
|
||||
) -> dict[str, int]:
|
||||
"""Remove a definition and its whole subtree (tasks, runs, versions).
|
||||
|
||||
Used when the canonical file disappears: the directory is the source of
|
||||
truth, so the derived registry follows. Commits once.
|
||||
"""
|
||||
version_ids = list(
|
||||
session.exec(
|
||||
select(FlowVersion.id).where(FlowVersion.flow_id == definition.id)
|
||||
).all()
|
||||
)
|
||||
task_count = 0
|
||||
run_count = 0
|
||||
if version_ids:
|
||||
tasks = list(
|
||||
session.exec(
|
||||
select(ApprovalTask).where(ApprovalTask.version_id.in_(version_ids))
|
||||
).all()
|
||||
)
|
||||
runs = list(
|
||||
session.exec(
|
||||
select(FlowRun).where(FlowRun.version_id.in_(version_ids))
|
||||
).all()
|
||||
)
|
||||
task_count, run_count = len(tasks), len(runs)
|
||||
for row in (*tasks, *runs):
|
||||
session.delete(row)
|
||||
for version in session.exec(
|
||||
select(FlowVersion).where(FlowVersion.flow_id == definition.id)
|
||||
).all():
|
||||
session.delete(version)
|
||||
session.delete(definition)
|
||||
session.commit()
|
||||
return {
|
||||
"definitions": 1,
|
||||
"versions": len(version_ids),
|
||||
"runs": run_count,
|
||||
"tasks": task_count,
|
||||
}
|
||||
|
||||
|
||||
def validate_flow_json(flow_json: dict) -> None:
|
||||
"""Reject anything that is not a Langflow-export-shaped document."""
|
||||
if not isinstance(flow_json, dict):
|
||||
raise InvalidError("langflow_json must be an object")
|
||||
data = flow_json.get("data")
|
||||
|
|
@ -70,7 +145,7 @@ def create_version(
|
|||
session: Session, flow_id: UUID, data: FlowVersionCreate
|
||||
) -> FlowVersion:
|
||||
get_definition(session, flow_id)
|
||||
_validate_flow_json(data.langflow_json)
|
||||
validate_flow_json(data.langflow_json)
|
||||
max_no = session.exec(
|
||||
select(func.max(FlowVersion.version_no)).where(FlowVersion.flow_id == flow_id)
|
||||
).one()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue