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"]