mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat: workflow library - system graphs - wip
This commit is contained in:
parent
0a25efd054
commit
b0350e9bc8
@ -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.default_graphs import create_system_graphs
|
||||||
from ..services.shared.graph import GraphExecutionState, LibraryGraph
|
from ..services.shared.graph import GraphExecutionState, LibraryGraph
|
||||||
from ..services.shared.sqlite.sqlite_database import SqliteDatabase
|
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.urls.urls_default import LocalUrlService
|
||||||
from ..services.workflow_records.workflow_records_sqlite import SqliteWorkflowRecordsStorage
|
from ..services.workflow_records.workflow_records_sqlite import SqliteWorkflowRecordsStorage
|
||||||
from .events import FastAPIEventService
|
from .events import FastAPIEventService
|
||||||
@ -123,6 +124,7 @@ class ApiDependencies:
|
|||||||
)
|
)
|
||||||
|
|
||||||
create_system_graphs(services.graph_library)
|
create_system_graphs(services.graph_library)
|
||||||
|
create_system_workflows(workflow_records=services.workflow_records, logger=logger)
|
||||||
|
|
||||||
db.clean()
|
db.clean()
|
||||||
ApiDependencies.invoker = Invoker(services)
|
ApiDependencies.invoker = Invoker(services)
|
||||||
|
@ -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.shared.sqlite.sqlite_common import SQLiteDirection
|
||||||
from invokeai.app.services.workflow_records.workflow_records_common import (
|
from invokeai.app.services.workflow_records.workflow_records_common import (
|
||||||
Workflow,
|
Workflow,
|
||||||
|
WorkflowCategory,
|
||||||
WorkflowNotFoundError,
|
WorkflowNotFoundError,
|
||||||
WorkflowRecordDTO,
|
WorkflowRecordDTO,
|
||||||
WorkflowRecordListItemDTO,
|
WorkflowRecordListItemDTO,
|
||||||
@ -70,7 +71,7 @@ async def create_workflow(
|
|||||||
workflow: WorkflowWithoutID = Body(description="The workflow to create", embed=True),
|
workflow: WorkflowWithoutID = Body(description="The workflow to create", embed=True),
|
||||||
) -> WorkflowRecordDTO:
|
) -> WorkflowRecordDTO:
|
||||||
"""Creates a workflow"""
|
"""Creates a workflow"""
|
||||||
return ApiDependencies.invoker.services.workflow_records.create(workflow)
|
return ApiDependencies.invoker.services.workflow_records.create(workflow=workflow)
|
||||||
|
|
||||||
|
|
||||||
@workflows_router.get(
|
@workflows_router.get(
|
||||||
@ -85,9 +86,10 @@ async def list_workflows(
|
|||||||
per_page: int = Query(default=10, description="The number of workflows per page"),
|
per_page: int = Query(default=10, description="The number of workflows per page"),
|
||||||
order_by: WorkflowRecordOrderBy = Query(default=WorkflowRecordOrderBy.Name, description="The order by"),
|
order_by: WorkflowRecordOrderBy = Query(default=WorkflowRecordOrderBy.Name, description="The order by"),
|
||||||
direction: SQLiteDirection = Query(default=SQLiteDirection.Ascending, 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"),
|
filter_text: Optional[str] = Query(default=None, description="The name to filter by"),
|
||||||
) -> PaginatedResults[WorkflowRecordListItemDTO]:
|
) -> PaginatedResults[WorkflowRecordListItemDTO]:
|
||||||
"""Gets a page of workflows"""
|
"""Gets a page of workflows"""
|
||||||
return ApiDependencies.invoker.services.workflow_records.get_many(
|
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
|
||||||
)
|
)
|
||||||
|
787
invokeai/app/assets/workflows/Text_to_Image_SD15.json
Normal file
787
invokeai/app/assets/workflows/Text_to_Image_SD15.json
Normal file
@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
33
invokeai/app/services/shared/system_workflows.py
Normal file
33
invokeai/app/services/shared/system_workflows.py
Normal file
@ -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)
|
@ -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.shared.sqlite.sqlite_common import SQLiteDirection
|
||||||
from invokeai.app.services.workflow_records.workflow_records_common import (
|
from invokeai.app.services.workflow_records.workflow_records_common import (
|
||||||
Workflow,
|
Workflow,
|
||||||
|
WorkflowCategory,
|
||||||
WorkflowRecordDTO,
|
WorkflowRecordDTO,
|
||||||
WorkflowRecordListItemDTO,
|
WorkflowRecordListItemDTO,
|
||||||
WorkflowRecordOrderBy,
|
WorkflowRecordOrderBy,
|
||||||
@ -42,7 +43,13 @@ class WorkflowRecordsStorageBase(ABC):
|
|||||||
per_page: int,
|
per_page: int,
|
||||||
order_by: WorkflowRecordOrderBy,
|
order_by: WorkflowRecordOrderBy,
|
||||||
direction: SQLiteDirection,
|
direction: SQLiteDirection,
|
||||||
|
category: WorkflowCategory,
|
||||||
filter_text: Optional[str],
|
filter_text: Optional[str],
|
||||||
) -> PaginatedResults[WorkflowRecordListItemDTO]:
|
) -> PaginatedResults[WorkflowRecordListItemDTO]:
|
||||||
"""Gets many workflows."""
|
"""Gets many workflows."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def _add_system_workflow(self, workflow: Workflow) -> None:
|
||||||
|
"""Adds a system workflow. Internal use only."""
|
||||||
|
pass
|
||||||
|
@ -16,8 +16,27 @@ class ExposedField(BaseModel):
|
|||||||
fieldName: str
|
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):
|
class WorkflowMeta(BaseModel):
|
||||||
version: str = Field(description="The version of the workflow schema.")
|
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")
|
@field_validator("version")
|
||||||
def validate_version(cls, version: str):
|
def validate_version(cls, version: str):
|
||||||
@ -56,14 +75,17 @@ class Workflow(WorkflowWithoutID):
|
|||||||
WorkflowValidator = TypeAdapter(Workflow)
|
WorkflowValidator = TypeAdapter(Workflow)
|
||||||
|
|
||||||
|
|
||||||
class WorkflowRecordDTO(BaseModel):
|
class WorkflowRecordDTOBase(BaseModel):
|
||||||
workflow_id: str = Field(description="The id of the workflow.")
|
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.")
|
name: str = Field(description="The name of the workflow.")
|
||||||
created_at: Union[datetime.datetime, str] = Field(description="The created timestamp 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.")
|
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.")
|
opened_at: Union[datetime.datetime, str] = Field(description="The opened timestamp of the workflow.")
|
||||||
|
|
||||||
|
|
||||||
|
class WorkflowRecordDTO(WorkflowRecordDTOBase):
|
||||||
|
workflow: Workflow = Field(description="The workflow.")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls, data: dict[str, Any]) -> "WorkflowRecordDTO":
|
def from_dict(cls, data: dict[str, Any]) -> "WorkflowRecordDTO":
|
||||||
data["workflow"] = WorkflowValidator.validate_json(data.get("workflow", ""))
|
data["workflow"] = WorkflowValidator.validate_json(data.get("workflow", ""))
|
||||||
@ -73,26 +95,8 @@ class WorkflowRecordDTO(BaseModel):
|
|||||||
WorkflowRecordDTOValidator = TypeAdapter(WorkflowRecordDTO)
|
WorkflowRecordDTOValidator = TypeAdapter(WorkflowRecordDTO)
|
||||||
|
|
||||||
|
|
||||||
class WorkflowRecordListItemDTO(BaseModel):
|
class WorkflowRecordListItemDTO(WorkflowRecordDTOBase):
|
||||||
workflow_id: str = Field(description="The id of the workflow.")
|
|
||||||
name: str = Field(description="The name of the workflow.")
|
|
||||||
description: str = Field(description="The description of the workflow.")
|
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)
|
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"
|
|
||||||
|
@ -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_base import WorkflowRecordsStorageBase
|
||||||
from invokeai.app.services.workflow_records.workflow_records_common import (
|
from invokeai.app.services.workflow_records.workflow_records_common import (
|
||||||
Workflow,
|
Workflow,
|
||||||
|
WorkflowCategory,
|
||||||
WorkflowNotFoundError,
|
WorkflowNotFoundError,
|
||||||
WorkflowRecordDTO,
|
WorkflowRecordDTO,
|
||||||
WorkflowRecordListItemDTO,
|
WorkflowRecordListItemDTO,
|
||||||
@ -40,6 +41,7 @@ class SqliteWorkflowRecordsStorage(WorkflowRecordsStorageBase):
|
|||||||
""",
|
""",
|
||||||
(workflow_id,),
|
(workflow_id,),
|
||||||
)
|
)
|
||||||
|
self._conn.commit()
|
||||||
self._cursor.execute(
|
self._cursor.execute(
|
||||||
"""--sql
|
"""--sql
|
||||||
SELECT workflow_id, workflow, name, created_at, updated_at, opened_at
|
SELECT workflow_id, workflow, name, created_at, updated_at, opened_at
|
||||||
@ -60,6 +62,8 @@ class SqliteWorkflowRecordsStorage(WorkflowRecordsStorageBase):
|
|||||||
|
|
||||||
def create(self, workflow: WorkflowWithoutID) -> WorkflowRecordDTO:
|
def create(self, workflow: WorkflowWithoutID) -> WorkflowRecordDTO:
|
||||||
try:
|
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())
|
workflow_with_id = WorkflowValidator.validate_python(workflow.model_dump())
|
||||||
self._lock.acquire()
|
self._lock.acquire()
|
||||||
self._cursor.execute(
|
self._cursor.execute(
|
||||||
@ -70,10 +74,7 @@ class SqliteWorkflowRecordsStorage(WorkflowRecordsStorageBase):
|
|||||||
)
|
)
|
||||||
VALUES (?, ?);
|
VALUES (?, ?);
|
||||||
""",
|
""",
|
||||||
(
|
(workflow_with_id.id, workflow_with_id.model_dump_json()),
|
||||||
workflow_with_id.id,
|
|
||||||
workflow_with_id.model_dump_json(),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
self._conn.commit()
|
self._conn.commit()
|
||||||
except Exception:
|
except Exception:
|
||||||
@ -90,7 +91,7 @@ class SqliteWorkflowRecordsStorage(WorkflowRecordsStorageBase):
|
|||||||
"""--sql
|
"""--sql
|
||||||
UPDATE workflow_library
|
UPDATE workflow_library
|
||||||
SET workflow = ?
|
SET workflow = ?
|
||||||
WHERE workflow_id = ?;
|
WHERE workflow_id = ? AND category = "user";
|
||||||
""",
|
""",
|
||||||
(workflow.model_dump_json(), workflow.id),
|
(workflow.model_dump_json(), workflow.id),
|
||||||
)
|
)
|
||||||
@ -108,7 +109,7 @@ class SqliteWorkflowRecordsStorage(WorkflowRecordsStorageBase):
|
|||||||
self._cursor.execute(
|
self._cursor.execute(
|
||||||
"""--sql
|
"""--sql
|
||||||
DELETE from workflow_library
|
DELETE from workflow_library
|
||||||
WHERE workflow_id = ?;
|
WHERE workflow_id = ? AND category = "user";
|
||||||
""",
|
""",
|
||||||
(workflow_id,),
|
(workflow_id,),
|
||||||
)
|
)
|
||||||
@ -126,6 +127,7 @@ class SqliteWorkflowRecordsStorage(WorkflowRecordsStorageBase):
|
|||||||
per_page: int,
|
per_page: int,
|
||||||
order_by: WorkflowRecordOrderBy,
|
order_by: WorkflowRecordOrderBy,
|
||||||
direction: SQLiteDirection,
|
direction: SQLiteDirection,
|
||||||
|
category: WorkflowCategory,
|
||||||
filter_text: Optional[str] = None,
|
filter_text: Optional[str] = None,
|
||||||
) -> PaginatedResults[WorkflowRecordListItemDTO]:
|
) -> PaginatedResults[WorkflowRecordListItemDTO]:
|
||||||
try:
|
try:
|
||||||
@ -133,24 +135,27 @@ class SqliteWorkflowRecordsStorage(WorkflowRecordsStorageBase):
|
|||||||
# sanitize!
|
# sanitize!
|
||||||
assert order_by in WorkflowRecordOrderBy
|
assert order_by in WorkflowRecordOrderBy
|
||||||
assert direction in SQLiteDirection
|
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 = """
|
main_query = """
|
||||||
SELECT
|
SELECT
|
||||||
workflow_id,
|
workflow_id,
|
||||||
|
category,
|
||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
created_at,
|
created_at,
|
||||||
updated_at,
|
updated_at,
|
||||||
opened_at
|
opened_at
|
||||||
FROM workflow_library
|
FROM workflow_library
|
||||||
|
WHERE category = ?
|
||||||
"""
|
"""
|
||||||
main_params = []
|
main_params = [category.value]
|
||||||
count_params = []
|
count_params = [category.value]
|
||||||
stripped_filter_name = filter_text.strip() if filter_text else None
|
stripped_filter_name = filter_text.strip() if filter_text else None
|
||||||
if stripped_filter_name:
|
if stripped_filter_name:
|
||||||
filter_string = "%" + stripped_filter_name + "%"
|
filter_string = "%" + stripped_filter_name + "%"
|
||||||
main_query += " WHERE name LIKE ? OR description LIKE ? "
|
main_query += " AND name LIKE ? OR description LIKE ? "
|
||||||
count_query += " WHERE name LIKE ? OR description LIKE ?;"
|
count_query += " AND name LIKE ? OR description LIKE ?;"
|
||||||
main_params.extend([filter_string, filter_string])
|
main_params.extend([filter_string, filter_string])
|
||||||
count_params.extend([filter_string, filter_string])
|
count_params.extend([filter_string, filter_string])
|
||||||
|
|
||||||
@ -177,6 +182,28 @@ class SqliteWorkflowRecordsStorage(WorkflowRecordsStorageBase):
|
|||||||
finally:
|
finally:
|
||||||
self._lock.release()
|
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:
|
def _create_tables(self) -> None:
|
||||||
try:
|
try:
|
||||||
self._lock.acquire()
|
self._lock.acquire()
|
||||||
@ -191,6 +218,7 @@ class SqliteWorkflowRecordsStorage(WorkflowRecordsStorageBase):
|
|||||||
-- updated manually when retrieving workflow
|
-- updated manually when retrieving workflow
|
||||||
opened_at DATETIME NOT NULL DEFAULT(STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')),
|
opened_at DATETIME NOT NULL DEFAULT(STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')),
|
||||||
-- Generated columns, needed for indexing and searching
|
-- 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,
|
name TEXT GENERATED ALWAYS as (json_extract(workflow, '$.name')) VIRTUAL NOT NULL,
|
||||||
description TEXT GENERATED ALWAYS as (json_extract(workflow, '$.description')) 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);
|
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(
|
self._cursor.execute(
|
||||||
"""--sql
|
"""--sql
|
||||||
CREATE INDEX IF NOT EXISTS idx_workflow_library_name ON workflow_library(name);
|
CREATE INDEX IF NOT EXISTS idx_workflow_library_name ON workflow_library(name);
|
||||||
|
@ -128,6 +128,8 @@
|
|||||||
"random": "Random",
|
"random": "Random",
|
||||||
"reportBugLabel": "Report Bug",
|
"reportBugLabel": "Report Bug",
|
||||||
"safetensors": "Safetensors",
|
"safetensors": "Safetensors",
|
||||||
|
"save": "Save",
|
||||||
|
"saveAs": "Save As",
|
||||||
"settingsLabel": "Settings",
|
"settingsLabel": "Settings",
|
||||||
"simple": "Simple",
|
"simple": "Simple",
|
||||||
"somethingWentWrong": "Something went wrong",
|
"somethingWentWrong": "Something went wrong",
|
||||||
@ -1625,7 +1627,7 @@
|
|||||||
"unnamedWorkflow": "Unnamed Workflow",
|
"unnamedWorkflow": "Unnamed Workflow",
|
||||||
"downloadWorkflow": "Download Workflow",
|
"downloadWorkflow": "Download Workflow",
|
||||||
"saveWorkflow": "Save Workflow",
|
"saveWorkflow": "Save Workflow",
|
||||||
"duplicateWorkflow": "Duplicate Workflow",
|
"saveWorkflowAs": "Save Workflow As",
|
||||||
"problemSavingWorkflow": "Problem Saving Workflow",
|
"problemSavingWorkflow": "Problem Saving Workflow",
|
||||||
"workflowSaved": "Workflow Saved",
|
"workflowSaved": "Workflow Saved",
|
||||||
"noRecentWorkflows": "No Recent Workflows",
|
"noRecentWorkflows": "No Recent Workflows",
|
||||||
@ -1635,6 +1637,7 @@
|
|||||||
"loading": "Loading Workflows",
|
"loading": "Loading Workflows",
|
||||||
"noDescription": "No description",
|
"noDescription": "No description",
|
||||||
"searchWorkflows": "Search Workflows",
|
"searchWorkflows": "Search Workflows",
|
||||||
"clearWorkflowSearchFilter": "Clear Workflow Search Filter"
|
"clearWorkflowSearchFilter": "Clear Workflow Search Filter",
|
||||||
|
"workflowName": "Workflow Name"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import DownloadWorkflowButton from 'features/workflowLibrary/components/Download
|
|||||||
import UploadWorkflowButton from 'features/workflowLibrary/components/LoadWorkflowFromFileButton';
|
import UploadWorkflowButton from 'features/workflowLibrary/components/LoadWorkflowFromFileButton';
|
||||||
import ResetWorkflowButton from 'features/workflowLibrary/components/ResetWorkflowButton';
|
import ResetWorkflowButton from 'features/workflowLibrary/components/ResetWorkflowButton';
|
||||||
import SaveWorkflowButton from 'features/workflowLibrary/components/SaveWorkflowButton';
|
import SaveWorkflowButton from 'features/workflowLibrary/components/SaveWorkflowButton';
|
||||||
import DuplicateWorkflowButton from 'features/workflowLibrary/components/DuplicateWorkflowButton';
|
import SaveWorkflowAsButton from 'features/workflowLibrary/components/SaveWorkflowAsButton';
|
||||||
|
|
||||||
const TopCenterPanel = () => {
|
const TopCenterPanel = () => {
|
||||||
return (
|
return (
|
||||||
@ -20,7 +20,7 @@ const TopCenterPanel = () => {
|
|||||||
<DownloadWorkflowButton />
|
<DownloadWorkflowButton />
|
||||||
<UploadWorkflowButton />
|
<UploadWorkflowButton />
|
||||||
<SaveWorkflowButton />
|
<SaveWorkflowButton />
|
||||||
<DuplicateWorkflowButton />
|
<SaveWorkflowAsButton />
|
||||||
<ResetWorkflowButton />
|
<ResetWorkflowButton />
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
|
@ -1,33 +1,4 @@
|
|||||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
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 { SHARED_NODE_PROPERTIES } from 'features/nodes/types/constants';
|
||||||
import {
|
import {
|
||||||
BoardFieldValue,
|
BoardFieldValue,
|
||||||
@ -58,6 +29,35 @@ import {
|
|||||||
zNodeStatus,
|
zNodeStatus,
|
||||||
} from 'features/nodes/types/invocation';
|
} from 'features/nodes/types/invocation';
|
||||||
import { WorkflowV2 } from 'features/nodes/types/workflow';
|
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 { NodesState } from './types';
|
||||||
import { findConnectionToValidHandle } from './util/findConnectionToValidHandle';
|
import { findConnectionToValidHandle } from './util/findConnectionToValidHandle';
|
||||||
import { findUnoccupiedPosition } from './util/findUnoccupiedPosition';
|
import { findUnoccupiedPosition } from './util/findUnoccupiedPosition';
|
||||||
@ -81,7 +81,7 @@ const INITIAL_WORKFLOW: WorkflowV2 = {
|
|||||||
nodes: [],
|
nodes: [],
|
||||||
edges: [],
|
edges: [],
|
||||||
exposedFields: [],
|
exposedFields: [],
|
||||||
meta: { version: '2.0.0' },
|
meta: { version: '2.0.0', category: 'user' },
|
||||||
};
|
};
|
||||||
|
|
||||||
export const initialNodesState: NodesState = {
|
export const initialNodesState: NodesState = {
|
||||||
|
@ -13,6 +13,9 @@ export type XYPosition = z.infer<typeof zXYPosition>;
|
|||||||
|
|
||||||
export const zDimension = z.number().gt(0).nullish();
|
export const zDimension = z.number().gt(0).nullish();
|
||||||
export type Dimension = z.infer<typeof zDimension>;
|
export type Dimension = z.infer<typeof zDimension>;
|
||||||
|
|
||||||
|
export const zWorkflowCategory = z.enum(['user', 'system']);
|
||||||
|
export type WorkflowCategory = z.infer<typeof zWorkflowCategory>;
|
||||||
// #endregion
|
// #endregion
|
||||||
|
|
||||||
// #region Workflow Nodes
|
// #region Workflow Nodes
|
||||||
@ -85,6 +88,7 @@ export const zWorkflowV2 = z.object({
|
|||||||
edges: z.array(zWorkflowEdge),
|
edges: z.array(zWorkflowEdge),
|
||||||
exposedFields: z.array(zFieldIdentifier),
|
exposedFields: z.array(zFieldIdentifier),
|
||||||
meta: z.object({
|
meta: z.object({
|
||||||
|
category: zWorkflowCategory.default('user'),
|
||||||
version: z.literal('2.0.0'),
|
version: z.literal('2.0.0'),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
@ -8,11 +8,15 @@ import {
|
|||||||
import { fromZodError } from 'zod-validation-error';
|
import { fromZodError } from 'zod-validation-error';
|
||||||
import { parseify } from 'common/util/serialize';
|
import { parseify } from 'common/util/serialize';
|
||||||
import i18n from 'i18next';
|
import i18n from 'i18next';
|
||||||
|
import { cloneDeep } from 'lodash-es';
|
||||||
|
|
||||||
export const buildWorkflow = (nodesState: NodesState): WorkflowV2 => {
|
export const buildWorkflow = (nodesState: NodesState): WorkflowV2 => {
|
||||||
const { workflow: workflowMeta, nodes, edges } = nodesState;
|
const workflow = cloneDeep(nodesState.workflow);
|
||||||
const workflow: WorkflowV2 = {
|
const nodes = cloneDeep(nodesState.nodes);
|
||||||
...workflowMeta,
|
const edges = cloneDeep(nodesState.edges);
|
||||||
|
|
||||||
|
const newWorkflow: WorkflowV2 = {
|
||||||
|
...workflow,
|
||||||
nodes: [],
|
nodes: [],
|
||||||
edges: [],
|
edges: [],
|
||||||
};
|
};
|
||||||
@ -30,7 +34,7 @@ export const buildWorkflow = (nodesState: NodesState): WorkflowV2 => {
|
|||||||
logger('nodes').warn({ node: parseify(node) }, message);
|
logger('nodes').warn({ node: parseify(node) }, message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
workflow.nodes.push(result.data);
|
newWorkflow.nodes.push(result.data);
|
||||||
});
|
});
|
||||||
|
|
||||||
edges.forEach((edge) => {
|
edges.forEach((edge) => {
|
||||||
@ -42,8 +46,8 @@ export const buildWorkflow = (nodesState: NodesState): WorkflowV2 => {
|
|||||||
logger('nodes').warn({ edge: parseify(edge) }, message);
|
logger('nodes').warn({ edge: parseify(edge) }, message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
workflow.edges.push(result.data);
|
newWorkflow.edges.push(result.data);
|
||||||
});
|
});
|
||||||
|
|
||||||
return workflow;
|
return newWorkflow;
|
||||||
};
|
};
|
||||||
|
@ -66,6 +66,7 @@ const migrateV1toV2 = (workflowToMigrate: WorkflowV1): WorkflowV2 => {
|
|||||||
});
|
});
|
||||||
// Bump version
|
// Bump version
|
||||||
(workflowToMigrate as unknown as WorkflowV2).meta.version = '2.0.0';
|
(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
|
// Parsing strips out any extra properties not in the latest version
|
||||||
return zWorkflowV2.parse(workflowToMigrate);
|
return zWorkflowV2.parse(workflowToMigrate);
|
||||||
};
|
};
|
||||||
|
@ -39,6 +39,13 @@ export const validateWorkflow = (
|
|||||||
// Parse the raw workflow data & migrate it to the latest version
|
// Parse the raw workflow data & migrate it to the latest version
|
||||||
const _workflow = parseAndMigrateWorkflow(workflow);
|
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
|
// Now we can validate the graph
|
||||||
const { nodes, edges } = _workflow;
|
const { nodes, edges } = _workflow;
|
||||||
const warnings: WorkflowWarning[] = [];
|
const warnings: WorkflowWarning[] = [];
|
||||||
|
@ -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 (
|
|
||||||
<IAIIconButton
|
|
||||||
icon={<FaClone />}
|
|
||||||
onClick={duplicateWorkflow}
|
|
||||||
isLoading={isLoading}
|
|
||||||
tooltip={t('workflows.duplicateWorkflow')}
|
|
||||||
aria-label={t('workflows.duplicateWorkflow')}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default memo(DuplicateLibraryWorkflowButton);
|
|
@ -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<HTMLInputElement>(null);
|
||||||
|
|
||||||
|
const onSave = useCallback(async () => {
|
||||||
|
saveWorkflowAs({ name, onSuccess: onClose, onError: onClose });
|
||||||
|
}, [name, onClose, saveWorkflowAs]);
|
||||||
|
|
||||||
|
const onChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setName(e.target.value);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<IAIIconButton
|
||||||
|
icon={<FaClone />}
|
||||||
|
onClick={onOpen}
|
||||||
|
isLoading={isLoading}
|
||||||
|
tooltip={t('workflows.saveWorkflowAs')}
|
||||||
|
aria-label={t('workflows.saveWorkflowAs')}
|
||||||
|
/>
|
||||||
|
<AlertDialog
|
||||||
|
isOpen={isOpen}
|
||||||
|
onClose={onClose}
|
||||||
|
leastDestructiveRef={inputRef}
|
||||||
|
isCentered
|
||||||
|
>
|
||||||
|
<AlertDialogOverlay>
|
||||||
|
<AlertDialogContent>
|
||||||
|
<AlertDialogHeader fontSize="lg" fontWeight="bold">
|
||||||
|
{t('workflows.saveWorkflowAs')}
|
||||||
|
</AlertDialogHeader>
|
||||||
|
|
||||||
|
<AlertDialogBody>
|
||||||
|
<FormControl>
|
||||||
|
<FormLabel>{t('workflows.workflowName')}</FormLabel>
|
||||||
|
<Input
|
||||||
|
ref={inputRef}
|
||||||
|
value={name}
|
||||||
|
onChange={onChange}
|
||||||
|
placeholder={t('workflows.workflowName')}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</AlertDialogBody>
|
||||||
|
|
||||||
|
<AlertDialogFooter>
|
||||||
|
<IAIButton onClick={onClose}>{t('common.cancel')}</IAIButton>
|
||||||
|
<IAIButton colorScheme="accent" onClick={onSave} ml={3}>
|
||||||
|
{t('common.saveAs')}
|
||||||
|
</IAIButton>
|
||||||
|
</AlertDialogFooter>
|
||||||
|
</AlertDialogContent>
|
||||||
|
</AlertDialogOverlay>
|
||||||
|
</AlertDialog>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(SaveWorkflowAsButton);
|
@ -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 WorkflowLibraryListWrapper from 'features/workflowLibrary/components/WorkflowLibraryListWrapper';
|
||||||
import WorkflowLibrarySystemList from 'features/workflowLibrary/components/WorkflowLibrarySystemList';
|
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import WorkflowLibraryUserList from './WorkflowLibraryUserList';
|
|
||||||
|
|
||||||
const WorkflowLibraryContent = () => {
|
const WorkflowLibraryContent = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tabs w="full" h="full" isLazy>
|
<WorkflowLibraryListWrapper>
|
||||||
<TabList w="10rem" layerStyle="second" borderRadius="base" p={2}>
|
<WorkflowLibraryList />
|
||||||
<Tab>{t('workflows.user')}</Tab>
|
</WorkflowLibraryListWrapper>
|
||||||
<Tab>{t('workflows.system')}</Tab>
|
|
||||||
</TabList>
|
|
||||||
|
|
||||||
<TabPanels>
|
|
||||||
<TabPanel>
|
|
||||||
<WorkflowLibraryListWrapper>
|
|
||||||
<WorkflowLibraryUserList />
|
|
||||||
</WorkflowLibraryListWrapper>
|
|
||||||
</TabPanel>
|
|
||||||
<TabPanel>
|
|
||||||
<WorkflowLibraryListWrapper>
|
|
||||||
<WorkflowLibrarySystemList />
|
|
||||||
</WorkflowLibraryListWrapper>
|
|
||||||
</TabPanel>
|
|
||||||
</TabPanels>
|
|
||||||
</Tabs>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { CloseIcon } from '@chakra-ui/icons';
|
import { CloseIcon } from '@chakra-ui/icons';
|
||||||
import {
|
import {
|
||||||
|
ButtonGroup,
|
||||||
Divider,
|
Divider,
|
||||||
Flex,
|
Flex,
|
||||||
Heading,
|
|
||||||
IconButton,
|
IconButton,
|
||||||
Input,
|
Input,
|
||||||
InputGroup,
|
InputGroup,
|
||||||
@ -10,12 +10,14 @@ import {
|
|||||||
Spacer,
|
Spacer,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { SelectItem } from '@mantine/core';
|
import { SelectItem } from '@mantine/core';
|
||||||
|
import IAIButton from 'common/components/IAIButton';
|
||||||
import {
|
import {
|
||||||
IAINoContentFallback,
|
IAINoContentFallback,
|
||||||
IAINoContentFallbackWithSpinner,
|
IAINoContentFallbackWithSpinner,
|
||||||
} from 'common/components/IAIImageFallback';
|
} from 'common/components/IAIImageFallback';
|
||||||
import IAIMantineSelect from 'common/components/IAIMantineSelect';
|
import IAIMantineSelect from 'common/components/IAIMantineSelect';
|
||||||
import ScrollableContent from 'features/nodes/components/sidePanel/ScrollableContent';
|
import ScrollableContent from 'features/nodes/components/sidePanel/ScrollableContent';
|
||||||
|
import { WorkflowCategory } from 'features/nodes/types/workflow';
|
||||||
import WorkflowLibraryListItem from 'features/workflowLibrary/components/WorkflowLibraryListItem';
|
import WorkflowLibraryListItem from 'features/workflowLibrary/components/WorkflowLibraryListItem';
|
||||||
import WorkflowLibraryPagination from 'features/workflowLibrary/components/WorkflowLibraryPagination';
|
import WorkflowLibraryPagination from 'features/workflowLibrary/components/WorkflowLibraryPagination';
|
||||||
import { ChangeEvent, KeyboardEvent, memo, useCallback, useState } from 'react';
|
import { ChangeEvent, KeyboardEvent, memo, useCallback, useState } from 'react';
|
||||||
@ -40,19 +42,19 @@ const DIRECTION_DATA: SelectItem[] = [
|
|||||||
|
|
||||||
const WorkflowLibraryList = () => {
|
const WorkflowLibraryList = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const [category, setCategory] = useState<WorkflowCategory>('user');
|
||||||
const [page, setPage] = useState(0);
|
const [page, setPage] = useState(0);
|
||||||
const [filter_text, setFilterText] = useState('');
|
const [filter_text, setFilterText] = useState('');
|
||||||
const [order_by, setOrderBy] = useState<WorkflowRecordOrderBy>('opened_at');
|
const [order_by, setOrderBy] = useState<WorkflowRecordOrderBy>('opened_at');
|
||||||
const [direction, setDirection] = useState<SQLiteDirection>('ASC');
|
const [direction, setDirection] = useState<SQLiteDirection>('ASC');
|
||||||
const [debouncedFilterText] = useDebounce(filter_text, 500, {
|
const [debouncedFilterText] = useDebounce(filter_text, 500);
|
||||||
leading: true,
|
|
||||||
});
|
|
||||||
const { data, isLoading, isError, isFetching } = useListWorkflowsQuery({
|
const { data, isLoading, isError, isFetching } = useListWorkflowsQuery({
|
||||||
page,
|
page,
|
||||||
per_page: PER_PAGE,
|
per_page: PER_PAGE,
|
||||||
order_by,
|
order_by,
|
||||||
direction,
|
direction,
|
||||||
filter_name: debouncedFilterText,
|
category,
|
||||||
|
filter_text: debouncedFilterText,
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleChangeOrderBy = useCallback(
|
const handleChangeOrderBy = useCallback(
|
||||||
@ -102,6 +104,14 @@ const WorkflowLibraryList = () => {
|
|||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleSetUserCategory = useCallback(() => {
|
||||||
|
setCategory('user');
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleSetSystemCategory = useCallback(() => {
|
||||||
|
setCategory('system');
|
||||||
|
}, []);
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return <IAINoContentFallbackWithSpinner label={t('workflows.loading')} />;
|
return <IAINoContentFallbackWithSpinner label={t('workflows.loading')} />;
|
||||||
}
|
}
|
||||||
@ -113,7 +123,22 @@ const WorkflowLibraryList = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Flex gap={4} alignItems="center" h={10} flexShrink={0} flexGrow={0}>
|
<Flex gap={4} alignItems="center" h={10} flexShrink={0} flexGrow={0}>
|
||||||
<Heading size="md">{t('workflows.userWorkflows')}</Heading>
|
<ButtonGroup>
|
||||||
|
<IAIButton
|
||||||
|
variant={category === 'user' ? undefined : 'ghost'}
|
||||||
|
onClick={handleSetUserCategory}
|
||||||
|
isChecked={category === 'user'}
|
||||||
|
>
|
||||||
|
{t('workflows.userWorkflows')}
|
||||||
|
</IAIButton>
|
||||||
|
<IAIButton
|
||||||
|
variant={category === 'system' ? undefined : 'ghost'}
|
||||||
|
onClick={handleSetSystemCategory}
|
||||||
|
isChecked={category === 'system'}
|
||||||
|
>
|
||||||
|
{t('workflows.systemWorkflows')}
|
||||||
|
</IAIButton>
|
||||||
|
</ButtonGroup>
|
||||||
<Spacer />
|
<Spacer />
|
||||||
<InputGroup w="20rem">
|
<InputGroup w="20rem">
|
||||||
<Input
|
<Input
|
||||||
@ -166,7 +191,7 @@ const WorkflowLibraryList = () => {
|
|||||||
<Divider />
|
<Divider />
|
||||||
{data.items.length ? (
|
{data.items.length ? (
|
||||||
<ScrollableContent>
|
<ScrollableContent>
|
||||||
<Flex w="full" h="full" gap={2} flexDir="column">
|
<Flex w="full" h="full" gap={2} px={1} flexDir="column">
|
||||||
{data.items.map((w) => (
|
{data.items.map((w) => (
|
||||||
<WorkflowLibraryListItem key={w.workflow_id} workflowDTO={w} />
|
<WorkflowLibraryListItem key={w.workflow_id} workflowDTO={w} />
|
||||||
))}
|
))}
|
@ -72,14 +72,16 @@ const WorkflowLibraryListItem = ({ workflowDTO }: Props) => {
|
|||||||
>
|
>
|
||||||
{t('common.load')}
|
{t('common.load')}
|
||||||
</IAIButton>
|
</IAIButton>
|
||||||
<IAIButton
|
{workflowDTO.category === 'user' && (
|
||||||
colorScheme="error"
|
<IAIButton
|
||||||
onClick={handleDeleteWorkflow}
|
colorScheme="error"
|
||||||
isLoading={deleteWorkflowResult.isLoading}
|
onClick={handleDeleteWorkflow}
|
||||||
aria-label={t('workflows.deleteWorkflow')}
|
isLoading={deleteWorkflowResult.isLoading}
|
||||||
>
|
aria-label={t('workflows.deleteWorkflow')}
|
||||||
{t('common.delete')}
|
>
|
||||||
</IAIButton>
|
{t('common.delete')}
|
||||||
|
</IAIButton>
|
||||||
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
|
@ -3,35 +3,47 @@ import { useAppDispatch } from 'app/store/storeHooks';
|
|||||||
import { useWorkflow } from 'features/nodes/hooks/useWorkflow';
|
import { useWorkflow } from 'features/nodes/hooks/useWorkflow';
|
||||||
import { workflowLoaded } from 'features/nodes/store/nodesSlice';
|
import { workflowLoaded } from 'features/nodes/store/nodesSlice';
|
||||||
import { zWorkflowV2 } from 'features/nodes/types/workflow';
|
import { zWorkflowV2 } from 'features/nodes/types/workflow';
|
||||||
import { omit } from 'lodash-es';
|
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useCreateWorkflowMutation } from 'services/api/endpoints/workflows';
|
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 { t } = useTranslation();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const workflow = useWorkflow();
|
const workflow = useWorkflow();
|
||||||
const [createWorkflow, createWorkflowResult] = useCreateWorkflowMutation();
|
const [createWorkflow, createWorkflowResult] = useCreateWorkflowMutation();
|
||||||
const toaster = useAppToaster();
|
const toaster = useAppToaster();
|
||||||
const duplicateWorkflow = useCallback(async () => {
|
const saveWorkflowAs = useCallback(
|
||||||
try {
|
async ({ name: newName, onSuccess, onError }: Arg) => {
|
||||||
const data = await createWorkflow(omit(workflow, 'id')).unwrap();
|
try {
|
||||||
const createdWorkflow = zWorkflowV2.parse(data.workflow);
|
workflow.id = undefined;
|
||||||
dispatch(workflowLoaded(createdWorkflow));
|
workflow.name = newName;
|
||||||
toaster({
|
const data = await createWorkflow(workflow).unwrap();
|
||||||
title: t('workflows.workflowSaved'),
|
const createdWorkflow = zWorkflowV2.parse(data.workflow);
|
||||||
status: 'success',
|
dispatch(workflowLoaded(createdWorkflow));
|
||||||
});
|
onSuccess && onSuccess();
|
||||||
} catch (e) {
|
toaster({
|
||||||
toaster({
|
title: t('workflows.workflowSaved'),
|
||||||
title: t('workflows.problemSavingWorkflow'),
|
status: 'success',
|
||||||
status: 'error',
|
});
|
||||||
});
|
} catch (e) {
|
||||||
}
|
onError && onError();
|
||||||
}, [workflow, dispatch, toaster, t, createWorkflow]);
|
toaster({
|
||||||
|
title: t('workflows.problemSavingWorkflow'),
|
||||||
|
status: 'error',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[workflow, dispatch, toaster, t, createWorkflow]
|
||||||
|
);
|
||||||
return {
|
return {
|
||||||
duplicateWorkflow,
|
saveWorkflowAs,
|
||||||
isLoading: createWorkflowResult.isLoading,
|
isLoading: createWorkflowResult.isLoading,
|
||||||
isError: createWorkflowResult.isError,
|
isError: createWorkflowResult.isError,
|
||||||
};
|
};
|
||||||
|
File diff suppressed because one or more lines are too long
@ -165,6 +165,7 @@ version = { attr = "invokeai.version.__version__" }
|
|||||||
|
|
||||||
[tool.setuptools.package-data]
|
[tool.setuptools.package-data]
|
||||||
"invokeai.app.assets" = ["**/*.png"]
|
"invokeai.app.assets" = ["**/*.png"]
|
||||||
|
"invokeai.app.assets.workflows" = ["**/*.json"]
|
||||||
"invokeai.assets.fonts" = ["**/*.ttf"]
|
"invokeai.assets.fonts" = ["**/*.ttf"]
|
||||||
"invokeai.backend" = ["**.png"]
|
"invokeai.backend" = ["**.png"]
|
||||||
"invokeai.configs" = ["*.example", "**/*.yaml", "*.txt"]
|
"invokeai.configs" = ["*.example", "**/*.yaml", "*.txt"]
|
||||||
|
Loading…
Reference in New Issue
Block a user