kirche-einfach/backend/app/models/flow.py

88 lines
4 KiB
Python

"""Flow engine domain models (SQLModel tables)."""
from datetime import datetime
from uuid import UUID, uuid4
from sqlalchemy import JSON, Column, UniqueConstraint
from sqlmodel import Field, SQLModel
from .base import ts_column, utcnow
def _json_column() -> Column:
# Generic JSON: JSONB on Postgres, JSON/TEXT elsewhere.
return Column(JSON)
class FlowDefinition(SQLModel, table=True):
"""A named decision flow (e.g. 'Bauvorhaben pruefen'). Versions live separately."""
__tablename__ = "flow_definitions"
id: UUID = Field(default_factory=uuid4, primary_key=True)
name: str = Field(index=True, unique=True, max_length=200)
description: str = Field(default="", max_length=2000)
status: str = Field(default="active", max_length=20) # active | archived
# Filename under backend/flows/ this definition is synced from
# (e.g. "simple_rule.json"). None for definitions created via the API.
source_path: str | None = Field(default=None, max_length=255)
# Flow to run after a human approval on this flow's runs (nullable).
continuation_flow_id: UUID | None = Field(default=None, foreign_key="flow_definitions.id")
created_at: datetime = Field(default_factory=utcnow, sa_column=ts_column())
class FlowVersion(SQLModel, table=True):
"""An immutable version of a flow: the exact Langflow JSON that runs."""
__tablename__ = "flow_versions"
__table_args__ = (UniqueConstraint("flow_id", "version_no"),)
id: UUID = Field(default_factory=uuid4, primary_key=True)
flow_id: UUID = Field(foreign_key="flow_definitions.id", index=True)
version_no: int = Field(index=True)
langflow_json: dict = Field(default_factory=dict, sa_column=_json_column())
# Flow id under which LFX serves this version (set on deploy).
lfx_flow_id: str | None = Field(default=None, max_length=200)
deployed_at: datetime | None = Field(default=None, sa_column=ts_column())
author: str = Field(default="", max_length=200)
created_at: datetime = Field(default_factory=utcnow, sa_column=ts_column())
# Cached form schema for this exact JSON (see app/flows/input_schema.py).
# Computed at version creation; nullable for pre-Phase-3 rows.
input_schema: dict | None = Field(default=None, sa_column=_json_column())
class FlowRun(SQLModel, table=True):
"""One execution of a flow version."""
__tablename__ = "flow_runs"
id: UUID = Field(default_factory=uuid4, primary_key=True)
version_id: UUID = Field(foreign_key="flow_versions.id", index=True)
tenant_id: str = Field(default="default", index=True, max_length=200)
# running | completed | pending_approval | failed
status: str = Field(default="running", index=True, max_length=30)
input: dict | None = Field(default=None, sa_column=_json_column())
output: dict | None = Field(default=None, sa_column=_json_column())
session_id: str | None = Field(default=None, max_length=200)
created_at: datetime = Field(default_factory=utcnow, sa_column=ts_column())
finished_at: datetime | None = Field(default=None, sa_column=ts_column())
class ApprovalTask(SQLModel, table=True):
"""Human task created by a flow that reached a terminal approval state."""
__tablename__ = "approval_tasks"
id: UUID = Field(default_factory=uuid4, primary_key=True)
run_id: UUID = Field(foreign_key="flow_runs.id", index=True)
version_id: UUID = Field(foreign_key="flow_versions.id", index=True)
tenant_id: str = Field(default="default", index=True, max_length=200)
title: str = Field(default="", max_length=500)
payload: dict = Field(default_factory=dict, sa_column=_json_column())
continuation_flow_id: UUID | None = Field(default=None)
approver: str | None = Field(default=None, max_length=200)
# open | approved | rejected
status: str = Field(default="open", index=True, max_length=20)
decision_note: str = Field(default="", max_length=2000)
created_at: datetime = Field(default_factory=utcnow, sa_column=ts_column())
decided_at: datetime | None = Field(default=None, sa_column=ts_column())