diff --git a/invokeai/app/api/dependencies.py b/invokeai/app/api/dependencies.py
index d2ca877a45..bceaf43644 100644
--- a/invokeai/app/api/dependencies.py
+++ b/invokeai/app/api/dependencies.py
@@ -30,6 +30,7 @@ from ..services.session_queue.session_queue_sqlite import SqliteSessionQueue
from ..services.shared.default_graphs import create_system_graphs
from ..services.shared.graph import GraphExecutionState, LibraryGraph
from ..services.shared.sqlite.sqlite_database import SqliteDatabase
+from ..services.shared.system_workflows import create_system_workflows
from ..services.urls.urls_default import LocalUrlService
from ..services.workflow_records.workflow_records_sqlite import SqliteWorkflowRecordsStorage
from .events import FastAPIEventService
@@ -123,6 +124,7 @@ class ApiDependencies:
)
create_system_graphs(services.graph_library)
+ create_system_workflows(workflow_records=services.workflow_records, logger=logger)
db.clean()
ApiDependencies.invoker = Invoker(services)
diff --git a/invokeai/app/api/routers/workflows.py b/invokeai/app/api/routers/workflows.py
index 38fb659525..e757d3f317 100644
--- a/invokeai/app/api/routers/workflows.py
+++ b/invokeai/app/api/routers/workflows.py
@@ -7,6 +7,7 @@ from invokeai.app.services.shared.pagination import PaginatedResults
from invokeai.app.services.shared.sqlite.sqlite_common import SQLiteDirection
from invokeai.app.services.workflow_records.workflow_records_common import (
Workflow,
+ WorkflowCategory,
WorkflowNotFoundError,
WorkflowRecordDTO,
WorkflowRecordListItemDTO,
@@ -70,7 +71,7 @@ async def create_workflow(
workflow: WorkflowWithoutID = Body(description="The workflow to create", embed=True),
) -> WorkflowRecordDTO:
"""Creates a workflow"""
- return ApiDependencies.invoker.services.workflow_records.create(workflow)
+ return ApiDependencies.invoker.services.workflow_records.create(workflow=workflow)
@workflows_router.get(
@@ -85,9 +86,10 @@ async def list_workflows(
per_page: int = Query(default=10, description="The number of workflows per page"),
order_by: WorkflowRecordOrderBy = Query(default=WorkflowRecordOrderBy.Name, description="The order by"),
direction: SQLiteDirection = Query(default=SQLiteDirection.Ascending, description="The order by"),
+ category: WorkflowCategory = Query(default=WorkflowCategory.User, description="The category to get"),
filter_text: Optional[str] = Query(default=None, description="The name to filter by"),
) -> PaginatedResults[WorkflowRecordListItemDTO]:
"""Gets a page of workflows"""
return ApiDependencies.invoker.services.workflow_records.get_many(
- page=page, per_page=per_page, order_by=order_by, direction=direction, filter_text=filter_text
+ page=page, per_page=per_page, order_by=order_by, direction=direction, filter_text=filter_text, category=category
)
diff --git a/invokeai/app/assets/workflows/Text_to_Image_SD15.json b/invokeai/app/assets/workflows/Text_to_Image_SD15.json
new file mode 100644
index 0000000000..d660c576e9
--- /dev/null
+++ b/invokeai/app/assets/workflows/Text_to_Image_SD15.json
@@ -0,0 +1,787 @@
+{
+ "id": "af7030e2-c64f-4f03-b387-9634bb54ae5f",
+ "name": "Text to Image - SD1.5",
+ "author": "InvokeAI",
+ "description": "Sample text to image workflow for Stable Diffusion 1.5/2",
+ "version": "1.1.0",
+ "contact": "invoke@invoke.ai",
+ "tags": "text2image, SD1.5, SD2, default",
+ "notes": "",
+ "exposedFields": [
+ {
+ "nodeId": "c8d55139-f380-4695-b7f2-8b3d1e1e3db8",
+ "fieldName": "model"
+ },
+ {
+ "nodeId": "7d8bf987-284f-413a-b2fd-d825445a5d6c",
+ "fieldName": "prompt"
+ },
+ {
+ "nodeId": "93dc02a4-d05b-48ed-b99c-c9b616af3402",
+ "fieldName": "prompt"
+ },
+ {
+ "nodeId": "55705012-79b9-4aac-9f26-c0b10309785b",
+ "fieldName": "width"
+ },
+ {
+ "nodeId": "55705012-79b9-4aac-9f26-c0b10309785b",
+ "fieldName": "height"
+ }
+ ],
+ "meta": {
+ "category": "system",
+ "version": "2.0.0"
+ },
+ "nodes": [
+ {
+ "id": "93dc02a4-d05b-48ed-b99c-c9b616af3402",
+ "type": "invocation",
+ "data": {
+ "id": "93dc02a4-d05b-48ed-b99c-c9b616af3402",
+ "type": "compel",
+ "label": "Negative Compel Prompt",
+ "isOpen": true,
+ "notes": "",
+ "isIntermediate": true,
+ "useCache": true,
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "inputs": {
+ "prompt": {
+ "id": "7739aff6-26cb-4016-8897-5a1fb2305e4e",
+ "name": "prompt",
+ "fieldKind": "input",
+ "label": "Negative Prompt",
+ "type": {
+ "isCollection": false,
+ "isCollectionOrScalar": false,
+ "name": "StringField"
+ },
+ "value": ""
+ },
+ "clip": {
+ "id": "48d23dce-a6ae-472a-9f8c-22a714ea5ce0",
+ "name": "clip",
+ "fieldKind": "input",
+ "label": "",
+ "type": {
+ "isCollection": false,
+ "isCollectionOrScalar": false,
+ "name": "ClipField"
+ }
+ }
+ },
+ "outputs": {
+ "conditioning": {
+ "id": "37cf3a9d-f6b7-4b64-8ff6-2558c5ecc447",
+ "name": "conditioning",
+ "fieldKind": "output",
+ "type": {
+ "isCollection": false,
+ "isCollectionOrScalar": false,
+ "name": "ConditioningField"
+ }
+ }
+ }
+ },
+ "width": 320,
+ "height": 259,
+ "position": {
+ "x": 1000,
+ "y": 350
+ }
+ },
+ {
+ "id": "55705012-79b9-4aac-9f26-c0b10309785b",
+ "type": "invocation",
+ "data": {
+ "id": "55705012-79b9-4aac-9f26-c0b10309785b",
+ "type": "noise",
+ "label": "",
+ "isOpen": true,
+ "notes": "",
+ "isIntermediate": true,
+ "useCache": true,
+ "version": "1.0.1",
+ "nodePack": "invokeai",
+ "inputs": {
+ "seed": {
+ "id": "6431737c-918a-425d-a3b4-5d57e2f35d4d",
+ "name": "seed",
+ "fieldKind": "input",
+ "label": "",
+ "type": {
+ "isCollection": false,
+ "isCollectionOrScalar": false,
+ "name": "IntegerField"
+ },
+ "value": 0
+ },
+ "width": {
+ "id": "38fc5b66-fe6e-47c8-bba9-daf58e454ed7",
+ "name": "width",
+ "fieldKind": "input",
+ "label": "",
+ "type": {
+ "isCollection": false,
+ "isCollectionOrScalar": false,
+ "name": "IntegerField"
+ },
+ "value": 512
+ },
+ "height": {
+ "id": "16298330-e2bf-4872-a514-d6923df53cbb",
+ "name": "height",
+ "fieldKind": "input",
+ "label": "",
+ "type": {
+ "isCollection": false,
+ "isCollectionOrScalar": false,
+ "name": "IntegerField"
+ },
+ "value": 512
+ },
+ "use_cpu": {
+ "id": "c7c436d3-7a7a-4e76-91e4-c6deb271623c",
+ "name": "use_cpu",
+ "fieldKind": "input",
+ "label": "",
+ "type": {
+ "isCollection": false,
+ "isCollectionOrScalar": false,
+ "name": "BooleanField"
+ },
+ "value": true
+ }
+ },
+ "outputs": {
+ "noise": {
+ "id": "50f650dc-0184-4e23-a927-0497a96fe954",
+ "name": "noise",
+ "fieldKind": "output",
+ "type": {
+ "isCollection": false,
+ "isCollectionOrScalar": false,
+ "name": "LatentsField"
+ }
+ },
+ "width": {
+ "id": "bb8a452b-133d-42d1-ae4a-3843d7e4109a",
+ "name": "width",
+ "fieldKind": "output",
+ "type": {
+ "isCollection": false,
+ "isCollectionOrScalar": false,
+ "name": "IntegerField"
+ }
+ },
+ "height": {
+ "id": "35cfaa12-3b8b-4b7a-a884-327ff3abddd9",
+ "name": "height",
+ "fieldKind": "output",
+ "type": {
+ "isCollection": false,
+ "isCollectionOrScalar": false,
+ "name": "IntegerField"
+ }
+ }
+ }
+ },
+ "width": 320,
+ "height": 388,
+ "position": {
+ "x": 600,
+ "y": 325
+ }
+ },
+ {
+ "id": "c8d55139-f380-4695-b7f2-8b3d1e1e3db8",
+ "type": "invocation",
+ "data": {
+ "id": "c8d55139-f380-4695-b7f2-8b3d1e1e3db8",
+ "type": "main_model_loader",
+ "label": "",
+ "isOpen": true,
+ "notes": "",
+ "isIntermediate": true,
+ "useCache": true,
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "inputs": {
+ "model": {
+ "id": "993eabd2-40fd-44fe-bce7-5d0c7075ddab",
+ "name": "model",
+ "fieldKind": "input",
+ "label": "",
+ "type": {
+ "isCollection": false,
+ "isCollectionOrScalar": false,
+ "name": "MainModelField"
+ },
+ "value": {
+ "model_name": "stable-diffusion-v1-5",
+ "base_model": "sd-1",
+ "model_type": "main"
+ }
+ }
+ },
+ "outputs": {
+ "unet": {
+ "id": "5c18c9db-328d-46d0-8cb9-143391c410be",
+ "name": "unet",
+ "fieldKind": "output",
+ "type": {
+ "isCollection": false,
+ "isCollectionOrScalar": false,
+ "name": "UNetField"
+ }
+ },
+ "clip": {
+ "id": "6effcac0-ec2f-4bf5-a49e-a2c29cf921f4",
+ "name": "clip",
+ "fieldKind": "output",
+ "type": {
+ "isCollection": false,
+ "isCollectionOrScalar": false,
+ "name": "ClipField"
+ }
+ },
+ "vae": {
+ "id": "57683ba3-f5f5-4f58-b9a2-4b83dacad4a1",
+ "name": "vae",
+ "fieldKind": "output",
+ "type": {
+ "isCollection": false,
+ "isCollectionOrScalar": false,
+ "name": "VaeField"
+ }
+ }
+ }
+ },
+ "width": 320,
+ "height": 226,
+ "position": {
+ "x": 600,
+ "y": 25
+ }
+ },
+ {
+ "id": "7d8bf987-284f-413a-b2fd-d825445a5d6c",
+ "type": "invocation",
+ "data": {
+ "id": "7d8bf987-284f-413a-b2fd-d825445a5d6c",
+ "type": "compel",
+ "label": "Positive Compel Prompt",
+ "isOpen": true,
+ "notes": "",
+ "isIntermediate": true,
+ "useCache": true,
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "inputs": {
+ "prompt": {
+ "id": "7739aff6-26cb-4016-8897-5a1fb2305e4e",
+ "name": "prompt",
+ "fieldKind": "input",
+ "label": "Positive Prompt",
+ "type": {
+ "isCollection": false,
+ "isCollectionOrScalar": false,
+ "name": "StringField"
+ },
+ "value": "Super cute tiger cub, national geographic award-winning photograph"
+ },
+ "clip": {
+ "id": "48d23dce-a6ae-472a-9f8c-22a714ea5ce0",
+ "name": "clip",
+ "fieldKind": "input",
+ "label": "",
+ "type": {
+ "isCollection": false,
+ "isCollectionOrScalar": false,
+ "name": "ClipField"
+ }
+ }
+ },
+ "outputs": {
+ "conditioning": {
+ "id": "37cf3a9d-f6b7-4b64-8ff6-2558c5ecc447",
+ "name": "conditioning",
+ "fieldKind": "output",
+ "type": {
+ "isCollection": false,
+ "isCollectionOrScalar": false,
+ "name": "ConditioningField"
+ }
+ }
+ }
+ },
+ "width": 320,
+ "height": 259,
+ "position": {
+ "x": 1000,
+ "y": 25
+ }
+ },
+ {
+ "id": "ea94bc37-d995-4a83-aa99-4af42479f2f2",
+ "type": "invocation",
+ "data": {
+ "id": "ea94bc37-d995-4a83-aa99-4af42479f2f2",
+ "type": "rand_int",
+ "label": "Random Seed",
+ "isOpen": false,
+ "notes": "",
+ "isIntermediate": true,
+ "useCache": false,
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "inputs": {
+ "low": {
+ "id": "3ec65a37-60ba-4b6c-a0b2-553dd7a84b84",
+ "name": "low",
+ "fieldKind": "input",
+ "label": "",
+ "type": {
+ "isCollection": false,
+ "isCollectionOrScalar": false,
+ "name": "IntegerField"
+ },
+ "value": 0
+ },
+ "high": {
+ "id": "085f853a-1a5f-494d-8bec-e4ba29a3f2d1",
+ "name": "high",
+ "fieldKind": "input",
+ "label": "",
+ "type": {
+ "isCollection": false,
+ "isCollectionOrScalar": false,
+ "name": "IntegerField"
+ },
+ "value": 2147483647
+ }
+ },
+ "outputs": {
+ "value": {
+ "id": "812ade4d-7699-4261-b9fc-a6c9d2ab55ee",
+ "name": "value",
+ "fieldKind": "output",
+ "type": {
+ "isCollection": false,
+ "isCollectionOrScalar": false,
+ "name": "IntegerField"
+ }
+ }
+ }
+ },
+ "width": 320,
+ "height": 32,
+ "position": {
+ "x": 600,
+ "y": 275
+ }
+ },
+ {
+ "id": "eea2702a-19fb-45b5-9d75-56b4211ec03c",
+ "type": "invocation",
+ "data": {
+ "id": "eea2702a-19fb-45b5-9d75-56b4211ec03c",
+ "type": "denoise_latents",
+ "label": "",
+ "isOpen": true,
+ "notes": "",
+ "isIntermediate": true,
+ "useCache": true,
+ "version": "1.4.0",
+ "nodePack": "invokeai",
+ "inputs": {
+ "positive_conditioning": {
+ "id": "90b7f4f8-ada7-4028-8100-d2e54f192052",
+ "name": "positive_conditioning",
+ "fieldKind": "input",
+ "label": "",
+ "type": {
+ "isCollection": false,
+ "isCollectionOrScalar": false,
+ "name": "ConditioningField"
+ }
+ },
+ "negative_conditioning": {
+ "id": "9393779e-796c-4f64-b740-902a1177bf53",
+ "name": "negative_conditioning",
+ "fieldKind": "input",
+ "label": "",
+ "type": {
+ "isCollection": false,
+ "isCollectionOrScalar": false,
+ "name": "ConditioningField"
+ }
+ },
+ "noise": {
+ "id": "8e17f1e5-4f98-40b1-b7f4-86aeeb4554c1",
+ "name": "noise",
+ "fieldKind": "input",
+ "label": "",
+ "type": {
+ "isCollection": false,
+ "isCollectionOrScalar": false,
+ "name": "LatentsField"
+ }
+ },
+ "steps": {
+ "id": "9b63302d-6bd2-42c9-ac13-9b1afb51af88",
+ "name": "steps",
+ "fieldKind": "input",
+ "label": "",
+ "type": {
+ "isCollection": false,
+ "isCollectionOrScalar": false,
+ "name": "IntegerField"
+ },
+ "value": 50
+ },
+ "cfg_scale": {
+ "id": "87dd04d3-870e-49e1-98bf-af003a810109",
+ "name": "cfg_scale",
+ "fieldKind": "input",
+ "label": "",
+ "type": {
+ "isCollection": false,
+ "isCollectionOrScalar": true,
+ "name": "FloatField"
+ },
+ "value": 7.5
+ },
+ "denoising_start": {
+ "id": "f369d80f-4931-4740-9bcd-9f0620719fab",
+ "name": "denoising_start",
+ "fieldKind": "input",
+ "label": "",
+ "type": {
+ "isCollection": false,
+ "isCollectionOrScalar": false,
+ "name": "FloatField"
+ },
+ "value": 0
+ },
+ "denoising_end": {
+ "id": "747d10e5-6f02-445c-994c-0604d814de8c",
+ "name": "denoising_end",
+ "fieldKind": "input",
+ "label": "",
+ "type": {
+ "isCollection": false,
+ "isCollectionOrScalar": false,
+ "name": "FloatField"
+ },
+ "value": 1
+ },
+ "scheduler": {
+ "id": "1de84a4e-3a24-4ec8-862b-16ce49633b9b",
+ "name": "scheduler",
+ "fieldKind": "input",
+ "label": "",
+ "type": {
+ "isCollection": false,
+ "isCollectionOrScalar": false,
+ "name": "SchedulerField"
+ },
+ "value": "unipc"
+ },
+ "unet": {
+ "id": "ffa6fef4-3ce2-4bdb-9296-9a834849489b",
+ "name": "unet",
+ "fieldKind": "input",
+ "label": "",
+ "type": {
+ "isCollection": false,
+ "isCollectionOrScalar": false,
+ "name": "UNetField"
+ }
+ },
+ "control": {
+ "id": "077b64cb-34be-4fcc-83f2-e399807a02bd",
+ "name": "control",
+ "fieldKind": "input",
+ "label": "",
+ "type": {
+ "isCollection": false,
+ "isCollectionOrScalar": true,
+ "name": "ControlField"
+ }
+ },
+ "ip_adapter": {
+ "id": "1d6948f7-3a65-4a65-a20c-768b287251aa",
+ "name": "ip_adapter",
+ "fieldKind": "input",
+ "label": "",
+ "type": {
+ "isCollection": false,
+ "isCollectionOrScalar": true,
+ "name": "IPAdapterField"
+ }
+ },
+ "t2i_adapter": {
+ "id": "75e67b09-952f-4083-aaf4-6b804d690412",
+ "name": "t2i_adapter",
+ "fieldKind": "input",
+ "label": "",
+ "type": {
+ "isCollection": false,
+ "isCollectionOrScalar": true,
+ "name": "T2IAdapterField"
+ }
+ },
+ "latents": {
+ "id": "334d4ba3-5a99-4195-82c5-86fb3f4f7d43",
+ "name": "latents",
+ "fieldKind": "input",
+ "label": "",
+ "type": {
+ "isCollection": false,
+ "isCollectionOrScalar": false,
+ "name": "LatentsField"
+ }
+ },
+ "denoise_mask": {
+ "id": "0d3dbdbf-b014-4e95-8b18-ff2ff9cb0bfa",
+ "name": "denoise_mask",
+ "fieldKind": "input",
+ "label": "",
+ "type": {
+ "isCollection": false,
+ "isCollectionOrScalar": false,
+ "name": "DenoiseMaskField"
+ }
+ }
+ },
+ "outputs": {
+ "latents": {
+ "id": "70fa5bbc-0c38-41bb-861a-74d6d78d2f38",
+ "name": "latents",
+ "fieldKind": "output",
+ "type": {
+ "isCollection": false,
+ "isCollectionOrScalar": false,
+ "name": "LatentsField"
+ }
+ },
+ "width": {
+ "id": "98ee0e6c-82aa-4e8f-8be5-dc5f00ee47f0",
+ "name": "width",
+ "fieldKind": "output",
+ "type": {
+ "isCollection": false,
+ "isCollectionOrScalar": false,
+ "name": "IntegerField"
+ }
+ },
+ "height": {
+ "id": "e8cb184a-5e1a-47c8-9695-4b8979564f5d",
+ "name": "height",
+ "fieldKind": "output",
+ "type": {
+ "isCollection": false,
+ "isCollectionOrScalar": false,
+ "name": "IntegerField"
+ }
+ }
+ }
+ },
+ "width": 320,
+ "height": 646,
+ "position": {
+ "x": 1400,
+ "y": 25
+ }
+ },
+ {
+ "id": "58c957f5-0d01-41fc-a803-b2bbf0413d4f",
+ "type": "invocation",
+ "data": {
+ "id": "58c957f5-0d01-41fc-a803-b2bbf0413d4f",
+ "type": "l2i",
+ "label": "",
+ "isOpen": true,
+ "notes": "",
+ "isIntermediate": false,
+ "useCache": true,
+ "version": "1.2.0",
+ "nodePack": "invokeai",
+ "inputs": {
+ "metadata": {
+ "id": "ab375f12-0042-4410-9182-29e30db82c85",
+ "name": "metadata",
+ "fieldKind": "input",
+ "label": "",
+ "type": {
+ "isCollection": false,
+ "isCollectionOrScalar": false,
+ "name": "MetadataField"
+ }
+ },
+ "latents": {
+ "id": "3a7e7efd-bff5-47d7-9d48-615127afee78",
+ "name": "latents",
+ "fieldKind": "input",
+ "label": "",
+ "type": {
+ "isCollection": false,
+ "isCollectionOrScalar": false,
+ "name": "LatentsField"
+ }
+ },
+ "vae": {
+ "id": "a1f5f7a1-0795-4d58-b036-7820c0b0ef2b",
+ "name": "vae",
+ "fieldKind": "input",
+ "label": "",
+ "type": {
+ "isCollection": false,
+ "isCollectionOrScalar": false,
+ "name": "VaeField"
+ }
+ },
+ "tiled": {
+ "id": "da52059a-0cee-4668-942f-519aa794d739",
+ "name": "tiled",
+ "fieldKind": "input",
+ "label": "",
+ "type": {
+ "isCollection": false,
+ "isCollectionOrScalar": false,
+ "name": "BooleanField"
+ },
+ "value": false
+ },
+ "fp32": {
+ "id": "c4841df3-b24e-4140-be3b-ccd454c2522c",
+ "name": "fp32",
+ "fieldKind": "input",
+ "label": "",
+ "type": {
+ "isCollection": false,
+ "isCollectionOrScalar": false,
+ "name": "BooleanField"
+ },
+ "value": true
+ }
+ },
+ "outputs": {
+ "image": {
+ "id": "72d667d0-cf85-459d-abf2-28bd8b823fe7",
+ "name": "image",
+ "fieldKind": "output",
+ "type": {
+ "isCollection": false,
+ "isCollectionOrScalar": false,
+ "name": "ImageField"
+ }
+ },
+ "width": {
+ "id": "c8c907d8-1066-49d1-b9a6-83bdcd53addc",
+ "name": "width",
+ "fieldKind": "output",
+ "type": {
+ "isCollection": false,
+ "isCollectionOrScalar": false,
+ "name": "IntegerField"
+ }
+ },
+ "height": {
+ "id": "230f359c-b4ea-436c-b372-332d7dcdca85",
+ "name": "height",
+ "fieldKind": "output",
+ "type": {
+ "isCollection": false,
+ "isCollectionOrScalar": false,
+ "name": "IntegerField"
+ }
+ }
+ }
+ },
+ "width": 320,
+ "height": 266,
+ "position": {
+ "x": 1800,
+ "y": 25
+ }
+ }
+ ],
+ "edges": [
+ {
+ "id": "reactflow__edge-ea94bc37-d995-4a83-aa99-4af42479f2f2value-55705012-79b9-4aac-9f26-c0b10309785bseed",
+ "source": "ea94bc37-d995-4a83-aa99-4af42479f2f2",
+ "target": "55705012-79b9-4aac-9f26-c0b10309785b",
+ "type": "default",
+ "sourceHandle": "value",
+ "targetHandle": "seed"
+ },
+ {
+ "id": "reactflow__edge-c8d55139-f380-4695-b7f2-8b3d1e1e3db8clip-7d8bf987-284f-413a-b2fd-d825445a5d6cclip",
+ "source": "c8d55139-f380-4695-b7f2-8b3d1e1e3db8",
+ "target": "7d8bf987-284f-413a-b2fd-d825445a5d6c",
+ "type": "default",
+ "sourceHandle": "clip",
+ "targetHandle": "clip"
+ },
+ {
+ "id": "reactflow__edge-c8d55139-f380-4695-b7f2-8b3d1e1e3db8clip-93dc02a4-d05b-48ed-b99c-c9b616af3402clip",
+ "source": "c8d55139-f380-4695-b7f2-8b3d1e1e3db8",
+ "target": "93dc02a4-d05b-48ed-b99c-c9b616af3402",
+ "type": "default",
+ "sourceHandle": "clip",
+ "targetHandle": "clip"
+ },
+ {
+ "id": "reactflow__edge-55705012-79b9-4aac-9f26-c0b10309785bnoise-eea2702a-19fb-45b5-9d75-56b4211ec03cnoise",
+ "source": "55705012-79b9-4aac-9f26-c0b10309785b",
+ "target": "eea2702a-19fb-45b5-9d75-56b4211ec03c",
+ "type": "default",
+ "sourceHandle": "noise",
+ "targetHandle": "noise"
+ },
+ {
+ "id": "reactflow__edge-7d8bf987-284f-413a-b2fd-d825445a5d6cconditioning-eea2702a-19fb-45b5-9d75-56b4211ec03cpositive_conditioning",
+ "source": "7d8bf987-284f-413a-b2fd-d825445a5d6c",
+ "target": "eea2702a-19fb-45b5-9d75-56b4211ec03c",
+ "type": "default",
+ "sourceHandle": "conditioning",
+ "targetHandle": "positive_conditioning"
+ },
+ {
+ "id": "reactflow__edge-93dc02a4-d05b-48ed-b99c-c9b616af3402conditioning-eea2702a-19fb-45b5-9d75-56b4211ec03cnegative_conditioning",
+ "source": "93dc02a4-d05b-48ed-b99c-c9b616af3402",
+ "target": "eea2702a-19fb-45b5-9d75-56b4211ec03c",
+ "type": "default",
+ "sourceHandle": "conditioning",
+ "targetHandle": "negative_conditioning"
+ },
+ {
+ "id": "reactflow__edge-c8d55139-f380-4695-b7f2-8b3d1e1e3db8unet-eea2702a-19fb-45b5-9d75-56b4211ec03cunet",
+ "source": "c8d55139-f380-4695-b7f2-8b3d1e1e3db8",
+ "target": "eea2702a-19fb-45b5-9d75-56b4211ec03c",
+ "type": "default",
+ "sourceHandle": "unet",
+ "targetHandle": "unet"
+ },
+ {
+ "id": "reactflow__edge-eea2702a-19fb-45b5-9d75-56b4211ec03clatents-58c957f5-0d01-41fc-a803-b2bbf0413d4flatents",
+ "source": "eea2702a-19fb-45b5-9d75-56b4211ec03c",
+ "target": "58c957f5-0d01-41fc-a803-b2bbf0413d4f",
+ "type": "default",
+ "sourceHandle": "latents",
+ "targetHandle": "latents"
+ },
+ {
+ "id": "reactflow__edge-c8d55139-f380-4695-b7f2-8b3d1e1e3db8vae-58c957f5-0d01-41fc-a803-b2bbf0413d4fvae",
+ "source": "c8d55139-f380-4695-b7f2-8b3d1e1e3db8",
+ "target": "58c957f5-0d01-41fc-a803-b2bbf0413d4f",
+ "type": "default",
+ "sourceHandle": "vae",
+ "targetHandle": "vae"
+ }
+ ]
+}
diff --git a/invokeai/app/services/shared/system_workflows.py b/invokeai/app/services/shared/system_workflows.py
new file mode 100644
index 0000000000..e47a5e58cc
--- /dev/null
+++ b/invokeai/app/services/shared/system_workflows.py
@@ -0,0 +1,33 @@
+from logging import Logger
+from pathlib import Path
+
+import semver
+
+import invokeai.app.assets.workflows as system_workflows_dir
+from invokeai.app.services.workflow_records.workflow_records_base import WorkflowRecordsStorageBase
+from invokeai.app.services.workflow_records.workflow_records_common import WorkflowNotFoundError, WorkflowValidator
+
+system_workflows = Path(system_workflows_dir.__path__[0]).glob("*.json")
+
+
+def create_system_workflows(workflow_records: WorkflowRecordsStorageBase, logger: Logger) -> None:
+ """Creates the system workflows."""
+ for workflow_filename in system_workflows:
+ with open(workflow_filename, "rb") as f:
+ workflow_bytes = f.read()
+ if workflow_bytes is None:
+ raise ValueError(f"Could not find system workflow: {workflow_filename}")
+
+ new_workflow = WorkflowValidator.validate_json(workflow_bytes)
+
+ try:
+ installed_workflow = workflow_records.get(new_workflow.id).workflow
+ installed_version = semver.Version.parse(installed_workflow.version)
+ new_version = semver.Version.parse(new_workflow.version)
+
+ if new_version.compare(installed_version) > 0:
+ logger.info(f"Updating system workflow: {new_workflow.name}")
+ workflow_records._add_system_workflow(new_workflow)
+ except WorkflowNotFoundError:
+ logger.info(f"Installing system workflow: {new_workflow.name}")
+ workflow_records._add_system_workflow(new_workflow)
diff --git a/invokeai/app/services/workflow_records/workflow_records_base.py b/invokeai/app/services/workflow_records/workflow_records_base.py
index c9e033718d..ae4b6b37fa 100644
--- a/invokeai/app/services/workflow_records/workflow_records_base.py
+++ b/invokeai/app/services/workflow_records/workflow_records_base.py
@@ -5,6 +5,7 @@ from invokeai.app.services.shared.pagination import PaginatedResults
from invokeai.app.services.shared.sqlite.sqlite_common import SQLiteDirection
from invokeai.app.services.workflow_records.workflow_records_common import (
Workflow,
+ WorkflowCategory,
WorkflowRecordDTO,
WorkflowRecordListItemDTO,
WorkflowRecordOrderBy,
@@ -42,7 +43,13 @@ class WorkflowRecordsStorageBase(ABC):
per_page: int,
order_by: WorkflowRecordOrderBy,
direction: SQLiteDirection,
+ category: WorkflowCategory,
filter_text: Optional[str],
) -> PaginatedResults[WorkflowRecordListItemDTO]:
"""Gets many workflows."""
pass
+
+ @abstractmethod
+ def _add_system_workflow(self, workflow: Workflow) -> None:
+ """Adds a system workflow. Internal use only."""
+ pass
diff --git a/invokeai/app/services/workflow_records/workflow_records_common.py b/invokeai/app/services/workflow_records/workflow_records_common.py
index 4fd34f18b2..c29350794b 100644
--- a/invokeai/app/services/workflow_records/workflow_records_common.py
+++ b/invokeai/app/services/workflow_records/workflow_records_common.py
@@ -16,8 +16,27 @@ class ExposedField(BaseModel):
fieldName: str
+class WorkflowNotFoundError(Exception):
+ """Raised when a workflow is not found"""
+
+
+class WorkflowRecordOrderBy(str, Enum, metaclass=MetaEnum):
+ """The order by options for workflow records"""
+
+ CreatedAt = "created_at"
+ UpdatedAt = "updated_at"
+ OpenedAt = "opened_at"
+ Name = "name"
+
+
+class WorkflowCategory(str, Enum, metaclass=MetaEnum):
+ User = "user"
+ System = "system"
+
+
class WorkflowMeta(BaseModel):
version: str = Field(description="The version of the workflow schema.")
+ category: WorkflowCategory = Field(description="The category of the workflow (user or system).")
@field_validator("version")
def validate_version(cls, version: str):
@@ -56,14 +75,17 @@ class Workflow(WorkflowWithoutID):
WorkflowValidator = TypeAdapter(Workflow)
-class WorkflowRecordDTO(BaseModel):
+class WorkflowRecordDTOBase(BaseModel):
workflow_id: str = Field(description="The id of the workflow.")
- workflow: Workflow = Field(description="The workflow.")
name: str = Field(description="The name of the workflow.")
created_at: Union[datetime.datetime, str] = Field(description="The created timestamp of the workflow.")
updated_at: Union[datetime.datetime, str] = Field(description="The updated timestamp of the workflow.")
opened_at: Union[datetime.datetime, str] = Field(description="The opened timestamp of the workflow.")
+
+class WorkflowRecordDTO(WorkflowRecordDTOBase):
+ workflow: Workflow = Field(description="The workflow.")
+
@classmethod
def from_dict(cls, data: dict[str, Any]) -> "WorkflowRecordDTO":
data["workflow"] = WorkflowValidator.validate_json(data.get("workflow", ""))
@@ -73,26 +95,8 @@ class WorkflowRecordDTO(BaseModel):
WorkflowRecordDTOValidator = TypeAdapter(WorkflowRecordDTO)
-class WorkflowRecordListItemDTO(BaseModel):
- workflow_id: str = Field(description="The id of the workflow.")
- name: str = Field(description="The name of the workflow.")
+class WorkflowRecordListItemDTO(WorkflowRecordDTOBase):
description: str = Field(description="The description of the workflow.")
- created_at: Union[datetime.datetime, str] = Field(description="The created timestamp of the workflow.")
- updated_at: Union[datetime.datetime, str] = Field(description="The updated timestamp of the workflow.")
- opened_at: Union[datetime.datetime, str] = Field(description="The opened timestamp of the workflow.")
WorkflowRecordListItemDTOValidator = TypeAdapter(WorkflowRecordListItemDTO)
-
-
-class WorkflowNotFoundError(Exception):
- """Raised when a workflow is not found"""
-
-
-class WorkflowRecordOrderBy(str, Enum, metaclass=MetaEnum):
- """The order by options for workflow records"""
-
- CreatedAt = "created_at"
- UpdatedAt = "updated_at"
- OpenedAt = "opened_at"
- Name = "name"
diff --git a/invokeai/app/services/workflow_records/workflow_records_sqlite.py b/invokeai/app/services/workflow_records/workflow_records_sqlite.py
index 3499a35026..8b2c4dfee6 100644
--- a/invokeai/app/services/workflow_records/workflow_records_sqlite.py
+++ b/invokeai/app/services/workflow_records/workflow_records_sqlite.py
@@ -7,6 +7,7 @@ from invokeai.app.services.shared.sqlite.sqlite_database import SqliteDatabase
from invokeai.app.services.workflow_records.workflow_records_base import WorkflowRecordsStorageBase
from invokeai.app.services.workflow_records.workflow_records_common import (
Workflow,
+ WorkflowCategory,
WorkflowNotFoundError,
WorkflowRecordDTO,
WorkflowRecordListItemDTO,
@@ -40,6 +41,7 @@ class SqliteWorkflowRecordsStorage(WorkflowRecordsStorageBase):
""",
(workflow_id,),
)
+ self._conn.commit()
self._cursor.execute(
"""--sql
SELECT workflow_id, workflow, name, created_at, updated_at, opened_at
@@ -60,6 +62,8 @@ class SqliteWorkflowRecordsStorage(WorkflowRecordsStorageBase):
def create(self, workflow: WorkflowWithoutID) -> WorkflowRecordDTO:
try:
+ # Only user workflows may be created by this method
+ assert workflow.meta.category is WorkflowCategory.User
workflow_with_id = WorkflowValidator.validate_python(workflow.model_dump())
self._lock.acquire()
self._cursor.execute(
@@ -70,10 +74,7 @@ class SqliteWorkflowRecordsStorage(WorkflowRecordsStorageBase):
)
VALUES (?, ?);
""",
- (
- workflow_with_id.id,
- workflow_with_id.model_dump_json(),
- ),
+ (workflow_with_id.id, workflow_with_id.model_dump_json()),
)
self._conn.commit()
except Exception:
@@ -90,7 +91,7 @@ class SqliteWorkflowRecordsStorage(WorkflowRecordsStorageBase):
"""--sql
UPDATE workflow_library
SET workflow = ?
- WHERE workflow_id = ?;
+ WHERE workflow_id = ? AND category = "user";
""",
(workflow.model_dump_json(), workflow.id),
)
@@ -108,7 +109,7 @@ class SqliteWorkflowRecordsStorage(WorkflowRecordsStorageBase):
self._cursor.execute(
"""--sql
DELETE from workflow_library
- WHERE workflow_id = ?;
+ WHERE workflow_id = ? AND category = "user";
""",
(workflow_id,),
)
@@ -126,6 +127,7 @@ class SqliteWorkflowRecordsStorage(WorkflowRecordsStorageBase):
per_page: int,
order_by: WorkflowRecordOrderBy,
direction: SQLiteDirection,
+ category: WorkflowCategory,
filter_text: Optional[str] = None,
) -> PaginatedResults[WorkflowRecordListItemDTO]:
try:
@@ -133,24 +135,27 @@ class SqliteWorkflowRecordsStorage(WorkflowRecordsStorageBase):
# sanitize!
assert order_by in WorkflowRecordOrderBy
assert direction in SQLiteDirection
- count_query = "SELECT COUNT(*) FROM workflow_library"
+ assert category in WorkflowCategory
+ count_query = "SELECT COUNT(*) FROM workflow_library WHERE category = ?"
main_query = """
SELECT
workflow_id,
+ category,
name,
description,
created_at,
updated_at,
opened_at
FROM workflow_library
+ WHERE category = ?
"""
- main_params = []
- count_params = []
+ main_params = [category.value]
+ count_params = [category.value]
stripped_filter_name = filter_text.strip() if filter_text else None
if stripped_filter_name:
filter_string = "%" + stripped_filter_name + "%"
- main_query += " WHERE name LIKE ? OR description LIKE ? "
- count_query += " WHERE name LIKE ? OR description LIKE ?;"
+ main_query += " AND name LIKE ? OR description LIKE ? "
+ count_query += " AND name LIKE ? OR description LIKE ?;"
main_params.extend([filter_string, filter_string])
count_params.extend([filter_string, filter_string])
@@ -177,6 +182,28 @@ class SqliteWorkflowRecordsStorage(WorkflowRecordsStorageBase):
finally:
self._lock.release()
+ def _add_system_workflow(self, workflow: Workflow) -> None:
+ try:
+ self._lock.acquire()
+ # Only system workflows may be created by this method
+ assert workflow.meta.category is WorkflowCategory.System
+ self._cursor.execute(
+ """--sql
+ INSERT OR REPLACE INTO workflow_library (
+ workflow_id,
+ workflow
+ )
+ VALUES (?, ?);
+ """,
+ (workflow.id, workflow.model_dump_json()),
+ )
+ self._conn.commit()
+ except Exception:
+ self._conn.rollback()
+ raise
+ finally:
+ self._lock.release()
+
def _create_tables(self) -> None:
try:
self._lock.acquire()
@@ -191,6 +218,7 @@ class SqliteWorkflowRecordsStorage(WorkflowRecordsStorageBase):
-- updated manually when retrieving workflow
opened_at DATETIME NOT NULL DEFAULT(STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')),
-- Generated columns, needed for indexing and searching
+ category TEXT GENERATED ALWAYS as (json_extract(workflow, '$.meta.category')) VIRTUAL NOT NULL,
name TEXT GENERATED ALWAYS as (json_extract(workflow, '$.name')) VIRTUAL NOT NULL,
description TEXT GENERATED ALWAYS as (json_extract(workflow, '$.description')) VIRTUAL NOT NULL
);
@@ -225,6 +253,11 @@ class SqliteWorkflowRecordsStorage(WorkflowRecordsStorageBase):
CREATE INDEX IF NOT EXISTS idx_workflow_library_opened_at ON workflow_library(opened_at);
"""
)
+ self._cursor.execute(
+ """--sql
+ CREATE INDEX IF NOT EXISTS idx_workflow_library_category ON workflow_library(category);
+ """
+ )
self._cursor.execute(
"""--sql
CREATE INDEX IF NOT EXISTS idx_workflow_library_name ON workflow_library(name);
diff --git a/invokeai/frontend/web/public/locales/en.json b/invokeai/frontend/web/public/locales/en.json
index 269e3010b5..e734ae9e08 100644
--- a/invokeai/frontend/web/public/locales/en.json
+++ b/invokeai/frontend/web/public/locales/en.json
@@ -128,6 +128,8 @@
"random": "Random",
"reportBugLabel": "Report Bug",
"safetensors": "Safetensors",
+ "save": "Save",
+ "saveAs": "Save As",
"settingsLabel": "Settings",
"simple": "Simple",
"somethingWentWrong": "Something went wrong",
@@ -1625,7 +1627,7 @@
"unnamedWorkflow": "Unnamed Workflow",
"downloadWorkflow": "Download Workflow",
"saveWorkflow": "Save Workflow",
- "duplicateWorkflow": "Duplicate Workflow",
+ "saveWorkflowAs": "Save Workflow As",
"problemSavingWorkflow": "Problem Saving Workflow",
"workflowSaved": "Workflow Saved",
"noRecentWorkflows": "No Recent Workflows",
@@ -1635,6 +1637,7 @@
"loading": "Loading Workflows",
"noDescription": "No description",
"searchWorkflows": "Search Workflows",
- "clearWorkflowSearchFilter": "Clear Workflow Search Filter"
+ "clearWorkflowSearchFilter": "Clear Workflow Search Filter",
+ "workflowName": "Workflow Name"
}
}
diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopCenterPanel/TopCenterPanel.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopCenterPanel/TopCenterPanel.tsx
index 3481a1d9eb..e4c08da69b 100644
--- a/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopCenterPanel/TopCenterPanel.tsx
+++ b/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopCenterPanel/TopCenterPanel.tsx
@@ -4,7 +4,7 @@ import DownloadWorkflowButton from 'features/workflowLibrary/components/Download
import UploadWorkflowButton from 'features/workflowLibrary/components/LoadWorkflowFromFileButton';
import ResetWorkflowButton from 'features/workflowLibrary/components/ResetWorkflowButton';
import SaveWorkflowButton from 'features/workflowLibrary/components/SaveWorkflowButton';
-import DuplicateWorkflowButton from 'features/workflowLibrary/components/DuplicateWorkflowButton';
+import SaveWorkflowAsButton from 'features/workflowLibrary/components/SaveWorkflowAsButton';
const TopCenterPanel = () => {
return (
@@ -20,7 +20,7 @@ const TopCenterPanel = () => {
-
+
);
diff --git a/invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts b/invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts
index 11dbc53e97..562e32108f 100644
--- a/invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts
+++ b/invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts
@@ -1,33 +1,4 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
-import { cloneDeep, forEach, isEqual, uniqBy } from 'lodash-es';
-import {
- addEdge,
- applyEdgeChanges,
- applyNodeChanges,
- Connection,
- Edge,
- EdgeChange,
- EdgeRemoveChange,
- getConnectedEdges,
- getIncomers,
- getOutgoers,
- Node,
- NodeChange,
- OnConnectStartParams,
- SelectionMode,
- updateEdge,
- Viewport,
- XYPosition,
-} from 'reactflow';
-import { receivedOpenAPISchema } from 'services/api/thunks/schema';
-import {
- appSocketGeneratorProgress,
- appSocketInvocationComplete,
- appSocketInvocationError,
- appSocketInvocationStarted,
- appSocketQueueItemStatusChanged,
-} from 'services/events/actions';
-import { v4 as uuidv4 } from 'uuid';
import { SHARED_NODE_PROPERTIES } from 'features/nodes/types/constants';
import {
BoardFieldValue,
@@ -58,6 +29,35 @@ import {
zNodeStatus,
} from 'features/nodes/types/invocation';
import { WorkflowV2 } from 'features/nodes/types/workflow';
+import { cloneDeep, forEach, isEqual, uniqBy } from 'lodash-es';
+import {
+ addEdge,
+ applyEdgeChanges,
+ applyNodeChanges,
+ Connection,
+ Edge,
+ EdgeChange,
+ EdgeRemoveChange,
+ getConnectedEdges,
+ getIncomers,
+ getOutgoers,
+ Node,
+ NodeChange,
+ OnConnectStartParams,
+ SelectionMode,
+ updateEdge,
+ Viewport,
+ XYPosition,
+} from 'reactflow';
+import { receivedOpenAPISchema } from 'services/api/thunks/schema';
+import {
+ appSocketGeneratorProgress,
+ appSocketInvocationComplete,
+ appSocketInvocationError,
+ appSocketInvocationStarted,
+ appSocketQueueItemStatusChanged,
+} from 'services/events/actions';
+import { v4 as uuidv4 } from 'uuid';
import { NodesState } from './types';
import { findConnectionToValidHandle } from './util/findConnectionToValidHandle';
import { findUnoccupiedPosition } from './util/findUnoccupiedPosition';
@@ -81,7 +81,7 @@ const INITIAL_WORKFLOW: WorkflowV2 = {
nodes: [],
edges: [],
exposedFields: [],
- meta: { version: '2.0.0' },
+ meta: { version: '2.0.0', category: 'user' },
};
export const initialNodesState: NodesState = {
diff --git a/invokeai/frontend/web/src/features/nodes/types/workflow.ts b/invokeai/frontend/web/src/features/nodes/types/workflow.ts
index 05cf539291..d4ba400e97 100644
--- a/invokeai/frontend/web/src/features/nodes/types/workflow.ts
+++ b/invokeai/frontend/web/src/features/nodes/types/workflow.ts
@@ -13,6 +13,9 @@ export type XYPosition = z.infer;
export const zDimension = z.number().gt(0).nullish();
export type Dimension = z.infer;
+
+export const zWorkflowCategory = z.enum(['user', 'system']);
+export type WorkflowCategory = z.infer;
// #endregion
// #region Workflow Nodes
@@ -85,6 +88,7 @@ export const zWorkflowV2 = z.object({
edges: z.array(zWorkflowEdge),
exposedFields: z.array(zFieldIdentifier),
meta: z.object({
+ category: zWorkflowCategory.default('user'),
version: z.literal('2.0.0'),
}),
});
diff --git a/invokeai/frontend/web/src/features/nodes/util/workflow/buildWorkflow.ts b/invokeai/frontend/web/src/features/nodes/util/workflow/buildWorkflow.ts
index 6b2d148706..b5c0e918c6 100644
--- a/invokeai/frontend/web/src/features/nodes/util/workflow/buildWorkflow.ts
+++ b/invokeai/frontend/web/src/features/nodes/util/workflow/buildWorkflow.ts
@@ -8,11 +8,15 @@ import {
import { fromZodError } from 'zod-validation-error';
import { parseify } from 'common/util/serialize';
import i18n from 'i18next';
+import { cloneDeep } from 'lodash-es';
export const buildWorkflow = (nodesState: NodesState): WorkflowV2 => {
- const { workflow: workflowMeta, nodes, edges } = nodesState;
- const workflow: WorkflowV2 = {
- ...workflowMeta,
+ const workflow = cloneDeep(nodesState.workflow);
+ const nodes = cloneDeep(nodesState.nodes);
+ const edges = cloneDeep(nodesState.edges);
+
+ const newWorkflow: WorkflowV2 = {
+ ...workflow,
nodes: [],
edges: [],
};
@@ -30,7 +34,7 @@ export const buildWorkflow = (nodesState: NodesState): WorkflowV2 => {
logger('nodes').warn({ node: parseify(node) }, message);
return;
}
- workflow.nodes.push(result.data);
+ newWorkflow.nodes.push(result.data);
});
edges.forEach((edge) => {
@@ -42,8 +46,8 @@ export const buildWorkflow = (nodesState: NodesState): WorkflowV2 => {
logger('nodes').warn({ edge: parseify(edge) }, message);
return;
}
- workflow.edges.push(result.data);
+ newWorkflow.edges.push(result.data);
});
- return workflow;
+ return newWorkflow;
};
diff --git a/invokeai/frontend/web/src/features/nodes/util/workflow/migrations.ts b/invokeai/frontend/web/src/features/nodes/util/workflow/migrations.ts
index 61ddbca6dd..80458ac8af 100644
--- a/invokeai/frontend/web/src/features/nodes/util/workflow/migrations.ts
+++ b/invokeai/frontend/web/src/features/nodes/util/workflow/migrations.ts
@@ -66,6 +66,7 @@ const migrateV1toV2 = (workflowToMigrate: WorkflowV1): WorkflowV2 => {
});
// Bump version
(workflowToMigrate as unknown as WorkflowV2).meta.version = '2.0.0';
+ (workflowToMigrate as unknown as WorkflowV2).meta.category = 'user';
// Parsing strips out any extra properties not in the latest version
return zWorkflowV2.parse(workflowToMigrate);
};
diff --git a/invokeai/frontend/web/src/features/nodes/util/workflow/validateWorkflow.ts b/invokeai/frontend/web/src/features/nodes/util/workflow/validateWorkflow.ts
index 002a0feff4..eb665b78fe 100644
--- a/invokeai/frontend/web/src/features/nodes/util/workflow/validateWorkflow.ts
+++ b/invokeai/frontend/web/src/features/nodes/util/workflow/validateWorkflow.ts
@@ -39,6 +39,13 @@ export const validateWorkflow = (
// Parse the raw workflow data & migrate it to the latest version
const _workflow = parseAndMigrateWorkflow(workflow);
+ // System workflows are only allowed to be used as templates.
+ // If a system workflow is loaded, change its category to user and remove its ID so that we can save it as a user workflow.
+ if (_workflow.meta.category === 'system') {
+ _workflow.meta.category = 'user';
+ _workflow.id = undefined;
+ }
+
// Now we can validate the graph
const { nodes, edges } = _workflow;
const warnings: WorkflowWarning[] = [];
diff --git a/invokeai/frontend/web/src/features/workflowLibrary/components/DuplicateWorkflowButton.tsx b/invokeai/frontend/web/src/features/workflowLibrary/components/DuplicateWorkflowButton.tsx
deleted file mode 100644
index c3beb9a8e0..0000000000
--- a/invokeai/frontend/web/src/features/workflowLibrary/components/DuplicateWorkflowButton.tsx
+++ /dev/null
@@ -1,21 +0,0 @@
-import IAIIconButton from 'common/components/IAIIconButton';
-import { useDuplicateLibraryWorkflow } from 'features/workflowLibrary/hooks/useDuplicateWorkflow';
-import { memo } from 'react';
-import { useTranslation } from 'react-i18next';
-import { FaClone } from 'react-icons/fa';
-
-const DuplicateLibraryWorkflowButton = () => {
- const { t } = useTranslation();
- const { duplicateWorkflow, isLoading } = useDuplicateLibraryWorkflow();
- return (
- }
- onClick={duplicateWorkflow}
- isLoading={isLoading}
- tooltip={t('workflows.duplicateWorkflow')}
- aria-label={t('workflows.duplicateWorkflow')}
- />
- );
-};
-
-export default memo(DuplicateLibraryWorkflowButton);
diff --git a/invokeai/frontend/web/src/features/workflowLibrary/components/SaveWorkflowAsButton.tsx b/invokeai/frontend/web/src/features/workflowLibrary/components/SaveWorkflowAsButton.tsx
new file mode 100644
index 0000000000..6bff13a887
--- /dev/null
+++ b/invokeai/frontend/web/src/features/workflowLibrary/components/SaveWorkflowAsButton.tsx
@@ -0,0 +1,83 @@
+import {
+ AlertDialog,
+ AlertDialogBody,
+ AlertDialogContent,
+ AlertDialogFooter,
+ AlertDialogHeader,
+ AlertDialogOverlay,
+ FormControl,
+ FormLabel,
+ Input,
+ useDisclosure,
+} from '@chakra-ui/react';
+import { useAppSelector } from 'app/store/storeHooks';
+import IAIButton from 'common/components/IAIButton';
+import IAIIconButton from 'common/components/IAIIconButton';
+import { useSaveWorkflowAs } from 'features/workflowLibrary/hooks/useDuplicateWorkflow';
+import { ChangeEvent, memo, useCallback, useRef, useState } from 'react';
+import { useTranslation } from 'react-i18next';
+import { FaClone } from 'react-icons/fa';
+
+const SaveWorkflowAsButton = () => {
+ const currentName = useAppSelector((state) => state.nodes.workflow.name);
+ const { t } = useTranslation();
+ const { saveWorkflowAs, isLoading } = useSaveWorkflowAs();
+ const [name, setName] = useState(currentName.trim());
+ const { isOpen, onOpen, onClose } = useDisclosure();
+ const inputRef = useRef(null);
+
+ const onSave = useCallback(async () => {
+ saveWorkflowAs({ name, onSuccess: onClose, onError: onClose });
+ }, [name, onClose, saveWorkflowAs]);
+
+ const onChange = useCallback((e: ChangeEvent) => {
+ setName(e.target.value);
+ }, []);
+
+ return (
+ <>
+ }
+ onClick={onOpen}
+ isLoading={isLoading}
+ tooltip={t('workflows.saveWorkflowAs')}
+ aria-label={t('workflows.saveWorkflowAs')}
+ />
+
+
+
+
+ {t('workflows.saveWorkflowAs')}
+
+
+
+
+ {t('workflows.workflowName')}
+
+
+
+
+
+ {t('common.cancel')}
+
+ {t('common.saveAs')}
+
+
+
+
+
+ >
+ );
+};
+
+export default memo(SaveWorkflowAsButton);
diff --git a/invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryContent.tsx b/invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryContent.tsx
index b1183892f8..04e51397b0 100644
--- a/invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryContent.tsx
+++ b/invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryContent.tsx
@@ -1,33 +1,15 @@
-import { Tab, TabList, TabPanel, TabPanels, Tabs } from '@chakra-ui/react';
+import WorkflowLibraryList from 'features/workflowLibrary/components/WorkflowLibraryList';
import WorkflowLibraryListWrapper from 'features/workflowLibrary/components/WorkflowLibraryListWrapper';
-import WorkflowLibrarySystemList from 'features/workflowLibrary/components/WorkflowLibrarySystemList';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
-import WorkflowLibraryUserList from './WorkflowLibraryUserList';
const WorkflowLibraryContent = () => {
const { t } = useTranslation();
return (
-
-
- {t('workflows.user')}
- {t('workflows.system')}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
);
};
diff --git a/invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryUserList.tsx b/invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryList.tsx
similarity index 82%
rename from invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryUserList.tsx
rename to invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryList.tsx
index e67cb83543..831ebddc36 100644
--- a/invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryUserList.tsx
+++ b/invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryList.tsx
@@ -1,8 +1,8 @@
import { CloseIcon } from '@chakra-ui/icons';
import {
+ ButtonGroup,
Divider,
Flex,
- Heading,
IconButton,
Input,
InputGroup,
@@ -10,12 +10,14 @@ import {
Spacer,
} from '@chakra-ui/react';
import { SelectItem } from '@mantine/core';
+import IAIButton from 'common/components/IAIButton';
import {
IAINoContentFallback,
IAINoContentFallbackWithSpinner,
} from 'common/components/IAIImageFallback';
import IAIMantineSelect from 'common/components/IAIMantineSelect';
import ScrollableContent from 'features/nodes/components/sidePanel/ScrollableContent';
+import { WorkflowCategory } from 'features/nodes/types/workflow';
import WorkflowLibraryListItem from 'features/workflowLibrary/components/WorkflowLibraryListItem';
import WorkflowLibraryPagination from 'features/workflowLibrary/components/WorkflowLibraryPagination';
import { ChangeEvent, KeyboardEvent, memo, useCallback, useState } from 'react';
@@ -40,19 +42,19 @@ const DIRECTION_DATA: SelectItem[] = [
const WorkflowLibraryList = () => {
const { t } = useTranslation();
+ const [category, setCategory] = useState('user');
const [page, setPage] = useState(0);
const [filter_text, setFilterText] = useState('');
const [order_by, setOrderBy] = useState('opened_at');
const [direction, setDirection] = useState('ASC');
- const [debouncedFilterText] = useDebounce(filter_text, 500, {
- leading: true,
- });
+ const [debouncedFilterText] = useDebounce(filter_text, 500);
const { data, isLoading, isError, isFetching } = useListWorkflowsQuery({
page,
per_page: PER_PAGE,
order_by,
direction,
- filter_name: debouncedFilterText,
+ category,
+ filter_text: debouncedFilterText,
});
const handleChangeOrderBy = useCallback(
@@ -102,6 +104,14 @@ const WorkflowLibraryList = () => {
[]
);
+ const handleSetUserCategory = useCallback(() => {
+ setCategory('user');
+ }, []);
+
+ const handleSetSystemCategory = useCallback(() => {
+ setCategory('system');
+ }, []);
+
if (isLoading) {
return ;
}
@@ -113,7 +123,22 @@ const WorkflowLibraryList = () => {
return (
<>
- {t('workflows.userWorkflows')}
+
+
+ {t('workflows.userWorkflows')}
+
+
+ {t('workflows.systemWorkflows')}
+
+
{
{data.items.length ? (
-
+
{data.items.map((w) => (
))}
diff --git a/invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryListItem.tsx b/invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryListItem.tsx
index 7de0c2dac2..f400a7a338 100644
--- a/invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryListItem.tsx
+++ b/invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryListItem.tsx
@@ -72,14 +72,16 @@ const WorkflowLibraryListItem = ({ workflowDTO }: Props) => {
>
{t('common.load')}
-
- {t('common.delete')}
-
+ {workflowDTO.category === 'user' && (
+
+ {t('common.delete')}
+
+ )}
);
diff --git a/invokeai/frontend/web/src/features/workflowLibrary/hooks/useDuplicateWorkflow.ts b/invokeai/frontend/web/src/features/workflowLibrary/hooks/useDuplicateWorkflow.ts
index decef66920..20b3fc980a 100644
--- a/invokeai/frontend/web/src/features/workflowLibrary/hooks/useDuplicateWorkflow.ts
+++ b/invokeai/frontend/web/src/features/workflowLibrary/hooks/useDuplicateWorkflow.ts
@@ -3,35 +3,47 @@ import { useAppDispatch } from 'app/store/storeHooks';
import { useWorkflow } from 'features/nodes/hooks/useWorkflow';
import { workflowLoaded } from 'features/nodes/store/nodesSlice';
import { zWorkflowV2 } from 'features/nodes/types/workflow';
-import { omit } from 'lodash-es';
import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { useCreateWorkflowMutation } from 'services/api/endpoints/workflows';
-export const useDuplicateLibraryWorkflow = () => {
+type Arg = {
+ name: string;
+ onSuccess?: () => void;
+ onError?: () => void;
+};
+
+export const useSaveWorkflowAs = () => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const workflow = useWorkflow();
const [createWorkflow, createWorkflowResult] = useCreateWorkflowMutation();
const toaster = useAppToaster();
- const duplicateWorkflow = useCallback(async () => {
- try {
- const data = await createWorkflow(omit(workflow, 'id')).unwrap();
- const createdWorkflow = zWorkflowV2.parse(data.workflow);
- dispatch(workflowLoaded(createdWorkflow));
- toaster({
- title: t('workflows.workflowSaved'),
- status: 'success',
- });
- } catch (e) {
- toaster({
- title: t('workflows.problemSavingWorkflow'),
- status: 'error',
- });
- }
- }, [workflow, dispatch, toaster, t, createWorkflow]);
+ const saveWorkflowAs = useCallback(
+ async ({ name: newName, onSuccess, onError }: Arg) => {
+ try {
+ workflow.id = undefined;
+ workflow.name = newName;
+ const data = await createWorkflow(workflow).unwrap();
+ const createdWorkflow = zWorkflowV2.parse(data.workflow);
+ dispatch(workflowLoaded(createdWorkflow));
+ onSuccess && onSuccess();
+ toaster({
+ title: t('workflows.workflowSaved'),
+ status: 'success',
+ });
+ } catch (e) {
+ onError && onError();
+ toaster({
+ title: t('workflows.problemSavingWorkflow'),
+ status: 'error',
+ });
+ }
+ },
+ [workflow, dispatch, toaster, t, createWorkflow]
+ );
return {
- duplicateWorkflow,
+ saveWorkflowAs,
isLoading: createWorkflowResult.isLoading,
isError: createWorkflowResult.isError,
};
diff --git a/invokeai/frontend/web/src/services/api/schema.d.ts b/invokeai/frontend/web/src/services/api/schema.d.ts
index f3cf56c4c6..9d323c456f 100644
--- a/invokeai/frontend/web/src/services/api/schema.d.ts
+++ b/invokeai/frontend/web/src/services/api/schema.d.ts
@@ -3243,7 +3243,7 @@ export type components = {
* @description The nodes in this graph
*/
nodes?: {
- [key: string]: components["schemas"]["MediapipeFaceProcessorInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ZoeDepthImageProcessorInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["AddInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["StringInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SDXLLoraLoaderInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["PidiImageProcessorInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["ONNXLatentsToImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["NormalbaeImageProcessorInvocation"] | components["schemas"]["HedImageProcessorInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["OnnxModelLoaderInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["SegmentAnythingProcessorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["LinearUIOutputInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["CannyImageProcessorInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["GraphInvocation"] | components["schemas"]["MidasDepthImageProcessorInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["OpenposeImageProcessorInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["LeresImageProcessorInvocation"] | components["schemas"]["ColorMapImageProcessorInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["LineartImageProcessorInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["LoraLoaderInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["FloatInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["VaeLoaderInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["StepParamEasingInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["MlsdImageProcessorInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["ONNXPromptInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["ONNXTextToLatentsInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["TileResamplerProcessorInvocation"] | components["schemas"]["ClipSkipInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["ContentShuffleImageProcessorInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LineartAnimeImageProcessorInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["BlankImageInvocation"];
+ [key: string]: components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["NormalbaeImageProcessorInvocation"] | components["schemas"]["StepParamEasingInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ClipSkipInvocation"] | components["schemas"]["OnnxModelLoaderInvocation"] | components["schemas"]["LoraLoaderInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["VaeLoaderInvocation"] | components["schemas"]["GraphInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["MidasDepthImageProcessorInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LineartImageProcessorInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ONNXTextToLatentsInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["OpenposeImageProcessorInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["SegmentAnythingProcessorInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["MlsdImageProcessorInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["CannyImageProcessorInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["AddInvocation"] | components["schemas"]["ONNXLatentsToImageInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["LeresImageProcessorInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ColorMapImageProcessorInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["FloatInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["SDXLLoraLoaderInvocation"] | components["schemas"]["HedImageProcessorInvocation"] | components["schemas"]["StringInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ContentShuffleImageProcessorInvocation"] | components["schemas"]["ONNXPromptInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["LinearUIOutputInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["ZoeDepthImageProcessorInvocation"] | components["schemas"]["MediapipeFaceProcessorInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["TileResamplerProcessorInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["LineartAnimeImageProcessorInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["PidiImageProcessorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"];
};
/**
* Edges
@@ -3280,7 +3280,7 @@ export type components = {
* @description The results of node executions
*/
results: {
- [key: string]: components["schemas"]["ColorOutput"] | components["schemas"]["FaceOffOutput"] | components["schemas"]["ControlOutput"] | components["schemas"]["NoiseOutput"] | components["schemas"]["IntegerCollectionOutput"] | components["schemas"]["LatentsCollectionOutput"] | components["schemas"]["BooleanCollectionOutput"] | components["schemas"]["FloatCollectionOutput"] | components["schemas"]["IPAdapterOutput"] | components["schemas"]["StringOutput"] | components["schemas"]["ModelLoaderOutput"] | components["schemas"]["SDXLRefinerModelLoaderOutput"] | components["schemas"]["FloatOutput"] | components["schemas"]["MetadataItemOutput"] | components["schemas"]["String2Output"] | components["schemas"]["SeamlessModeOutput"] | components["schemas"]["BooleanOutput"] | components["schemas"]["StringCollectionOutput"] | components["schemas"]["CLIPOutput"] | components["schemas"]["T2IAdapterOutput"] | components["schemas"]["ConditioningOutput"] | components["schemas"]["FaceMaskOutput"] | components["schemas"]["ClipSkipInvocationOutput"] | components["schemas"]["StringPosNegOutput"] | components["schemas"]["ONNXModelLoaderOutput"] | components["schemas"]["ConditioningCollectionOutput"] | components["schemas"]["CollectInvocationOutput"] | components["schemas"]["SDXLLoraLoaderOutput"] | components["schemas"]["IterateInvocationOutput"] | components["schemas"]["IntegerOutput"] | components["schemas"]["MetadataOutput"] | components["schemas"]["GraphInvocationOutput"] | components["schemas"]["ImageCollectionOutput"] | components["schemas"]["SchedulerOutput"] | components["schemas"]["UNetOutput"] | components["schemas"]["LoraLoaderOutput"] | components["schemas"]["DenoiseMaskOutput"] | components["schemas"]["LatentsOutput"] | components["schemas"]["ImageOutput"] | components["schemas"]["ColorCollectionOutput"] | components["schemas"]["VAEOutput"] | components["schemas"]["SDXLModelLoaderOutput"];
+ [key: string]: components["schemas"]["LoraLoaderOutput"] | components["schemas"]["BooleanCollectionOutput"] | components["schemas"]["ConditioningOutput"] | components["schemas"]["FloatCollectionOutput"] | components["schemas"]["DenoiseMaskOutput"] | components["schemas"]["ClipSkipInvocationOutput"] | components["schemas"]["ConditioningCollectionOutput"] | components["schemas"]["FloatOutput"] | components["schemas"]["String2Output"] | components["schemas"]["SDXLModelLoaderOutput"] | components["schemas"]["BooleanOutput"] | components["schemas"]["MetadataOutput"] | components["schemas"]["StringPosNegOutput"] | components["schemas"]["ONNXModelLoaderOutput"] | components["schemas"]["ColorCollectionOutput"] | components["schemas"]["GraphInvocationOutput"] | components["schemas"]["StringOutput"] | components["schemas"]["ControlOutput"] | components["schemas"]["VAEOutput"] | components["schemas"]["SchedulerOutput"] | components["schemas"]["ImageCollectionOutput"] | components["schemas"]["ColorOutput"] | components["schemas"]["SDXLRefinerModelLoaderOutput"] | components["schemas"]["FaceOffOutput"] | components["schemas"]["NoiseOutput"] | components["schemas"]["FaceMaskOutput"] | components["schemas"]["IntegerOutput"] | components["schemas"]["LatentsCollectionOutput"] | components["schemas"]["ImageOutput"] | components["schemas"]["SeamlessModeOutput"] | components["schemas"]["LatentsOutput"] | components["schemas"]["UNetOutput"] | components["schemas"]["MetadataItemOutput"] | components["schemas"]["CollectInvocationOutput"] | components["schemas"]["ModelLoaderOutput"] | components["schemas"]["StringCollectionOutput"] | components["schemas"]["IntegerCollectionOutput"] | components["schemas"]["CLIPOutput"] | components["schemas"]["SDXLLoraLoaderOutput"] | components["schemas"]["IPAdapterOutput"] | components["schemas"]["IterateInvocationOutput"] | components["schemas"]["T2IAdapterOutput"];
};
/**
* Errors
@@ -9488,6 +9488,11 @@ export type components = {
*/
id?: string;
};
+ /**
+ * WorkflowCategory
+ * @enum {string}
+ */
+ WorkflowCategory: "user" | "system";
/** WorkflowMeta */
WorkflowMeta: {
/**
@@ -9503,8 +9508,6 @@ export type components = {
* @description The id of the workflow.
*/
workflow_id: string;
- /** @description The workflow. */
- workflow: components["schemas"]["Workflow"];
/**
* Name
* @description The name of the workflow.
@@ -9525,6 +9528,10 @@ export type components = {
* @description The opened timestamp of the workflow.
*/
opened_at: string;
+ /** @description The category of the workflow (user or system). */
+ category: components["schemas"]["WorkflowCategory"];
+ /** @description The workflow. */
+ workflow: components["schemas"]["Workflow"];
};
/** WorkflowRecordListItemDTO */
WorkflowRecordListItemDTO: {
@@ -9538,11 +9545,6 @@ export type components = {
* @description The name of the workflow.
*/
name: string;
- /**
- * Description
- * @description The description of the workflow.
- */
- description: string;
/**
* Created At
* @description The created timestamp of the workflow.
@@ -9558,6 +9560,13 @@ export type components = {
* @description The opened timestamp of the workflow.
*/
opened_at: string;
+ /** @description The category of the workflow (user or system). */
+ category: components["schemas"]["WorkflowCategory"];
+ /**
+ * Description
+ * @description The description of the workflow.
+ */
+ description: string;
};
/**
* WorkflowRecordOrderBy
@@ -9866,6 +9875,18 @@ export type components = {
* @enum {string}
*/
UIType: "SDXLMainModelField" | "SDXLRefinerModelField" | "ONNXModelField" | "VAEModelField" | "LoRAModelField" | "ControlNetModelField" | "IPAdapterModelField" | "SchedulerField" | "AnyField" | "CollectionField" | "CollectionItemField" | "DEPRECATED_Boolean" | "DEPRECATED_Color" | "DEPRECATED_Conditioning" | "DEPRECATED_Control" | "DEPRECATED_Float" | "DEPRECATED_Image" | "DEPRECATED_Integer" | "DEPRECATED_Latents" | "DEPRECATED_String" | "DEPRECATED_BooleanCollection" | "DEPRECATED_ColorCollection" | "DEPRECATED_ConditioningCollection" | "DEPRECATED_ControlCollection" | "DEPRECATED_FloatCollection" | "DEPRECATED_ImageCollection" | "DEPRECATED_IntegerCollection" | "DEPRECATED_LatentsCollection" | "DEPRECATED_StringCollection" | "DEPRECATED_BooleanPolymorphic" | "DEPRECATED_ColorPolymorphic" | "DEPRECATED_ConditioningPolymorphic" | "DEPRECATED_ControlPolymorphic" | "DEPRECATED_FloatPolymorphic" | "DEPRECATED_ImagePolymorphic" | "DEPRECATED_IntegerPolymorphic" | "DEPRECATED_LatentsPolymorphic" | "DEPRECATED_StringPolymorphic" | "DEPRECATED_MainModel" | "DEPRECATED_UNet" | "DEPRECATED_Vae" | "DEPRECATED_CLIP" | "DEPRECATED_Collection" | "DEPRECATED_CollectionItem" | "DEPRECATED_Enum" | "DEPRECATED_WorkflowField" | "DEPRECATED_IsIntermediate" | "DEPRECATED_BoardField" | "DEPRECATED_MetadataItem" | "DEPRECATED_MetadataItemCollection" | "DEPRECATED_MetadataItemPolymorphic" | "DEPRECATED_MetadataDict";
+ /**
+ * IPAdapterModelFormat
+ * @description An enumeration.
+ * @enum {string}
+ */
+ IPAdapterModelFormat: "invokeai";
+ /**
+ * ControlNetModelFormat
+ * @description An enumeration.
+ * @enum {string}
+ */
+ ControlNetModelFormat: "checkpoint" | "diffusers";
/**
* StableDiffusionOnnxModelFormat
* @description An enumeration.
@@ -9878,30 +9899,6 @@ export type components = {
* @enum {string}
*/
StableDiffusion1ModelFormat: "checkpoint" | "diffusers";
- /**
- * ControlNetModelFormat
- * @description An enumeration.
- * @enum {string}
- */
- ControlNetModelFormat: "checkpoint" | "diffusers";
- /**
- * CLIPVisionModelFormat
- * @description An enumeration.
- * @enum {string}
- */
- CLIPVisionModelFormat: "diffusers";
- /**
- * IPAdapterModelFormat
- * @description An enumeration.
- * @enum {string}
- */
- IPAdapterModelFormat: "invokeai";
- /**
- * T2IAdapterModelFormat
- * @description An enumeration.
- * @enum {string}
- */
- T2IAdapterModelFormat: "diffusers";
/**
* StableDiffusionXLModelFormat
* @description An enumeration.
@@ -9914,6 +9911,18 @@ export type components = {
* @enum {string}
*/
StableDiffusion2ModelFormat: "checkpoint" | "diffusers";
+ /**
+ * CLIPVisionModelFormat
+ * @description An enumeration.
+ * @enum {string}
+ */
+ CLIPVisionModelFormat: "diffusers";
+ /**
+ * T2IAdapterModelFormat
+ * @description An enumeration.
+ * @enum {string}
+ */
+ T2IAdapterModelFormat: "diffusers";
};
responses: never;
parameters: never;
@@ -11732,8 +11741,10 @@ export type operations = {
order_by?: components["schemas"]["WorkflowRecordOrderBy"];
/** @description The order by */
direction?: components["schemas"]["SQLiteDirection"];
+ /** @description The category to get */
+ category?: components["schemas"]["WorkflowCategory"];
/** @description The name to filter by */
- filter_name?: string | null;
+ filter_text?: string | null;
};
};
responses: {
diff --git a/pyproject.toml b/pyproject.toml
index 20c25f6449..d28788b31d 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -165,6 +165,7 @@ version = { attr = "invokeai.version.__version__" }
[tool.setuptools.package-data]
"invokeai.app.assets" = ["**/*.png"]
+"invokeai.app.assets.workflows" = ["**/*.json"]
"invokeai.assets.fonts" = ["**/*.ttf"]
"invokeai.backend" = ["**.png"]
"invokeai.configs" = ["*.example", "**/*.yaml", "*.txt"]