"""ComfyUI workflow templates an administrator saved. A table rather than a list inside the settings group, for the reason `McpServer.tools_json` is *not* a table: that one is a cache of somebody else's document, replaced wholesale on every refresh, where each entry carries one decision. These are the opposite -- authored by hand, individually named, edited, reordered and deleted, and referenced by id from a chat. Everything a table gives for free is exactly what is wanted. Deliberately **no group access list**, unlike `CustomTool`. The whole feature is already behind one capability flag and one permission; a second access system covering which templates a person may pick would be a screen of checkboxes nobody asked for, and the thing being restricted is the shape of a picture. """ from __future__ import annotations from datetime import datetime from typing import Any from sqlalchemy import Boolean, DateTime, Integer, String, Text from sqlalchemy.orm import Mapped, mapped_column from lembas.db.base import Base, Timestamps, UUIDPrimaryKey from lembas.db.types import JSONDict class ImageWorkflow(UUIDPrimaryKey, Timestamps, Base): """One API-format ComfyUI workflow, with holes where the values go.""" __tablename__ = "image_workflows" # What the *model* names when it picks this one, so it is short and # lowercase for the same reason a tool's slug is: it lands in a schema enum # and is generated by something that spells inconsistently. slug: Mapped[str] = mapped_column(String(64), unique=True, nullable=False) name: Mapped[str] = mapped_column(String(120), nullable=False) # Sent to the model beside the slug, and the only thing it has to choose # with. "Photographic, SDXL, slow" is a choice; "workflow 2" is not. description: Mapped[str] = mapped_column(Text, default="") # The workflow itself, in ComfyUI's API format, with `{{placeholders}}` # where the parameters go. Stored parsed rather than as text so the admin # form can only ever save something that is valid JSON -- a template that # does not parse would fail at generation time, minutes later, in front of # somebody who was not editing it. workflow_json: Mapped[dict[str, Any]] = mapped_column(JSONDict, default=dict) enabled: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False) position: Mapped[int] = mapped_column(Integer, default=0, nullable=False) # The result of the last time somebody pressed Test, in the shape # `CustomTool` and `McpServer` already use, so the row reads the same way in # the list as theirs do. last_checked_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) last_error: Mapped[str] = mapped_column(Text, default="") def __repr__(self) -> str: return f""