mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
a514c9e28b
Update workflows handling for Workflow Library. **Updated Workflow Storage** "Embedded Workflows" are workflows associated with images, and are now only stored in the image files. "Library Workflows" are not associated with images, and are stored only in DB. This works out nicely. We have always saved workflows to files, but recently began saving them to the DB in addition to in image files. When that happened, we stopped reading workflows from files, so all the workflows that only existed in images were inaccessible. With this change, access to those workflows is restored, and no workflows are lost. **Updated Workflow Handling in Nodes** Prior to this change, workflows were embedded in images by passing the whole workflow JSON to a special workflow field on a node. In the node's `invoke()` function, the node was able to access this workflow and save it with the image. This (inaccurately) models workflows as a property of an image and is rather awkward technically. A workflow is now a property of a batch/session queue item. It is available in the InvocationContext and therefore available to all nodes during `invoke()`. **Database Migrations** Added a `SQLiteMigrator` class to handle database migrations. Migrations were needed to accomodate the DB-related changes in this PR. See the code for details. The `images`, `workflows` and `session_queue` tables required migrations for this PR, and are using the new migrator. Other tables/services are still creating tables themselves. A followup PR will adapt them to use the migrator. **Other/Support Changes** - Add a `has_workflow` column to `images` table to indicate that the image has an embedded workflow. - Add handling for retrieving the workflow from an image in python. The image file must be fetched, the workflow extracted, and then sent to client, avoiding needing the browser to parse the image file. With the `has_workflow` column, the UI knows if there is a workflow to be fetched, and only fetches when the user requests to load the workflow. - Add route to get the workflow from an image - Add CRUD service/routes for the library workflows - `workflow_images` table and services removed (no longer needed now that embedded workflows are not in the DB)
135 lines
5.6 KiB
Python
135 lines
5.6 KiB
Python
# Copyright (c) 2022 Kyle Schouviller (https://github.com/kyle0654)
|
|
|
|
from logging import Logger
|
|
|
|
from invokeai.backend.util.logging import InvokeAILogger
|
|
from invokeai.version.invokeai_version import __version__
|
|
|
|
from ..services.board_image_records.board_image_records_sqlite import SqliteBoardImageRecordStorage
|
|
from ..services.board_images.board_images_default import BoardImagesService
|
|
from ..services.board_records.board_records_sqlite import SqliteBoardRecordStorage
|
|
from ..services.boards.boards_default import BoardService
|
|
from ..services.config import InvokeAIAppConfig
|
|
from ..services.image_files.image_files_disk import DiskImageFileStorage
|
|
from ..services.image_records.image_records_sqlite import SqliteImageRecordStorage
|
|
from ..services.images.images_default import ImageService
|
|
from ..services.invocation_cache.invocation_cache_memory import MemoryInvocationCache
|
|
from ..services.invocation_processor.invocation_processor_default import DefaultInvocationProcessor
|
|
from ..services.invocation_queue.invocation_queue_memory import MemoryInvocationQueue
|
|
from ..services.invocation_services import InvocationServices
|
|
from ..services.invocation_stats.invocation_stats_default import InvocationStatsService
|
|
from ..services.invoker import Invoker
|
|
from ..services.item_storage.item_storage_sqlite import SqliteItemStorage
|
|
from ..services.latents_storage.latents_storage_disk import DiskLatentsStorage
|
|
from ..services.latents_storage.latents_storage_forward_cache import ForwardCacheLatentsStorage
|
|
from ..services.model_manager.model_manager_default import ModelManagerService
|
|
from ..services.model_records import ModelRecordServiceSQL
|
|
from ..services.names.names_default import SimpleNameService
|
|
from ..services.session_processor.session_processor_default import DefaultSessionProcessor
|
|
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.urls.urls_default import LocalUrlService
|
|
from ..services.workflow_records.workflow_records_sqlite import SqliteWorkflowRecordsStorage
|
|
from .events import FastAPIEventService
|
|
|
|
|
|
# TODO: is there a better way to achieve this?
|
|
def check_internet() -> bool:
|
|
"""
|
|
Return true if the internet is reachable.
|
|
It does this by pinging huggingface.co.
|
|
"""
|
|
import urllib.request
|
|
|
|
host = "http://huggingface.co"
|
|
try:
|
|
urllib.request.urlopen(host, timeout=1)
|
|
return True
|
|
except Exception:
|
|
return False
|
|
|
|
|
|
logger = InvokeAILogger.get_logger()
|
|
|
|
|
|
class ApiDependencies:
|
|
"""Contains and initializes all dependencies for the API"""
|
|
|
|
invoker: Invoker
|
|
|
|
@staticmethod
|
|
def initialize(config: InvokeAIAppConfig, event_handler_id: int, logger: Logger = logger):
|
|
logger.info(f"InvokeAI version {__version__}")
|
|
logger.info(f"Root directory = {str(config.root_path)}")
|
|
logger.debug(f"Internet connectivity is {config.internet_available}")
|
|
|
|
output_folder = config.output_path
|
|
|
|
db = SqliteDatabase(config, logger)
|
|
|
|
configuration = config
|
|
logger = logger
|
|
|
|
board_image_records = SqliteBoardImageRecordStorage(db=db)
|
|
board_images = BoardImagesService()
|
|
board_records = SqliteBoardRecordStorage(db=db)
|
|
boards = BoardService()
|
|
events = FastAPIEventService(event_handler_id)
|
|
graph_execution_manager = SqliteItemStorage[GraphExecutionState](db=db, table_name="graph_executions")
|
|
graph_library = SqliteItemStorage[LibraryGraph](db=db, table_name="graphs")
|
|
image_files = DiskImageFileStorage(f"{output_folder}/images")
|
|
image_records = SqliteImageRecordStorage(db=db)
|
|
images = ImageService()
|
|
invocation_cache = MemoryInvocationCache(max_cache_size=config.node_cache_size)
|
|
latents = ForwardCacheLatentsStorage(DiskLatentsStorage(f"{output_folder}/latents"))
|
|
model_manager = ModelManagerService(config, logger)
|
|
model_record_service = ModelRecordServiceSQL(db=db)
|
|
names = SimpleNameService()
|
|
performance_statistics = InvocationStatsService()
|
|
processor = DefaultInvocationProcessor()
|
|
queue = MemoryInvocationQueue()
|
|
session_processor = DefaultSessionProcessor()
|
|
session_queue = SqliteSessionQueue(db=db)
|
|
urls = LocalUrlService()
|
|
workflow_records = SqliteWorkflowRecordsStorage(db=db)
|
|
|
|
services = InvocationServices(
|
|
board_image_records=board_image_records,
|
|
board_images=board_images,
|
|
board_records=board_records,
|
|
boards=boards,
|
|
configuration=configuration,
|
|
events=events,
|
|
graph_execution_manager=graph_execution_manager,
|
|
graph_library=graph_library,
|
|
image_files=image_files,
|
|
image_records=image_records,
|
|
images=images,
|
|
invocation_cache=invocation_cache,
|
|
latents=latents,
|
|
logger=logger,
|
|
model_manager=model_manager,
|
|
model_records=model_record_service,
|
|
names=names,
|
|
performance_statistics=performance_statistics,
|
|
processor=processor,
|
|
queue=queue,
|
|
session_processor=session_processor,
|
|
session_queue=session_queue,
|
|
urls=urls,
|
|
workflow_records=workflow_records,
|
|
)
|
|
|
|
create_system_graphs(services.graph_library)
|
|
|
|
db.run_migrations()
|
|
db.clean()
|
|
ApiDependencies.invoker = Invoker(services)
|
|
|
|
@staticmethod
|
|
def shutdown():
|
|
if ApiDependencies.invoker:
|
|
ApiDependencies.invoker.stop()
|