2022-12-01 05:33:20 +00:00
|
|
|
# Copyright (c) 2022 Kyle Schouviller (https://github.com/kyle0654)
|
|
|
|
import asyncio
|
|
|
|
from inspect import signature
|
2023-03-03 06:02:00 +00:00
|
|
|
|
|
|
|
import uvicorn
|
2023-05-25 23:06:09 +00:00
|
|
|
|
2023-05-23 12:57:29 +00:00
|
|
|
from invokeai.backend.util.logging import InvokeAILogger
|
2022-12-01 05:33:20 +00:00
|
|
|
from fastapi import FastAPI
|
2023-03-03 06:02:00 +00:00
|
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
|
|
from fastapi.openapi.docs import get_redoc_html, get_swagger_ui_html
|
2022-12-01 05:33:20 +00:00
|
|
|
from fastapi.openapi.utils import get_openapi
|
|
|
|
from fastapi.staticfiles import StaticFiles
|
|
|
|
from fastapi_events.handlers.local import local_handler
|
2023-03-03 06:02:00 +00:00
|
|
|
from fastapi_events.middleware import EventHandlerASGIMiddleware
|
2023-05-22 20:48:17 +00:00
|
|
|
from pathlib import Path
|
2022-12-01 05:33:20 +00:00
|
|
|
from pydantic.schema import schema
|
2023-03-03 06:02:00 +00:00
|
|
|
|
2023-05-26 02:01:48 +00:00
|
|
|
import invokeai.frontend.web as web_dir
|
|
|
|
|
2023-03-03 06:02:00 +00:00
|
|
|
from .api.dependencies import ApiDependencies
|
2023-05-22 02:46:37 +00:00
|
|
|
from .api.routers import sessions, models, images
|
2022-12-01 05:33:20 +00:00
|
|
|
from .api.sockets import SocketIO
|
|
|
|
from .invocations.baseinvocation import BaseInvocation
|
2023-05-17 04:18:19 +00:00
|
|
|
from .services.config import InvokeAIAppConfig
|
2022-12-01 05:33:20 +00:00
|
|
|
|
2023-05-23 12:57:29 +00:00
|
|
|
logger = InvokeAILogger.getLogger()
|
2023-05-17 09:13:53 +00:00
|
|
|
|
2022-12-01 05:33:20 +00:00
|
|
|
# Create the app
|
|
|
|
# TODO: create this all in a method so configuration/etc. can be passed in?
|
2023-03-03 06:02:00 +00:00
|
|
|
app = FastAPI(title="Invoke AI", docs_url=None, redoc_url=None)
|
2022-12-01 05:33:20 +00:00
|
|
|
|
|
|
|
# Add event handler
|
|
|
|
event_handler_id: int = id(app)
|
|
|
|
app.add_middleware(
|
|
|
|
EventHandlerASGIMiddleware,
|
2023-03-03 06:02:00 +00:00
|
|
|
handlers=[
|
|
|
|
local_handler
|
|
|
|
], # TODO: consider doing this in services to support different configurations
|
|
|
|
middleware_id=event_handler_id,
|
|
|
|
)
|
2022-12-01 05:33:20 +00:00
|
|
|
|
|
|
|
socket_io = SocketIO(app)
|
2023-05-14 01:33:19 +00:00
|
|
|
|
2023-05-17 23:42:13 +00:00
|
|
|
# initialize config
|
|
|
|
# this is a module global
|
2023-05-26 00:41:26 +00:00
|
|
|
app_config = InvokeAIAppConfig.get_config()
|
|
|
|
app_config.parse_args()
|
2023-03-03 06:02:00 +00:00
|
|
|
|
2022-12-01 05:33:20 +00:00
|
|
|
# Add startup event to load dependencies
|
2023-03-03 06:02:00 +00:00
|
|
|
@app.on_event("startup")
|
2022-12-01 05:33:20 +00:00
|
|
|
async def startup_event():
|
2023-05-14 01:33:19 +00:00
|
|
|
app.add_middleware(
|
|
|
|
CORSMiddleware,
|
2023-05-17 23:42:13 +00:00
|
|
|
allow_origins=app_config.allow_origins,
|
|
|
|
allow_credentials=app_config.allow_credentials,
|
|
|
|
allow_methods=app_config.allow_methods,
|
|
|
|
allow_headers=app_config.allow_headers,
|
2023-05-14 01:33:19 +00:00
|
|
|
)
|
|
|
|
|
2022-12-01 05:33:20 +00:00
|
|
|
ApiDependencies.initialize(
|
2023-05-17 23:42:13 +00:00
|
|
|
config=app_config, event_handler_id=event_handler_id, logger=logger
|
2022-12-01 05:33:20 +00:00
|
|
|
)
|
|
|
|
|
2023-03-03 06:02:00 +00:00
|
|
|
|
2022-12-01 05:33:20 +00:00
|
|
|
# Shut down threads
|
2023-03-03 06:02:00 +00:00
|
|
|
@app.on_event("shutdown")
|
2022-12-01 05:33:20 +00:00
|
|
|
async def shutdown_event():
|
|
|
|
ApiDependencies.shutdown()
|
|
|
|
|
2023-03-03 06:02:00 +00:00
|
|
|
|
2022-12-01 05:33:20 +00:00
|
|
|
# Include all routers
|
|
|
|
# TODO: REMOVE
|
|
|
|
# app.include_router(
|
|
|
|
# invocation.invocation_router,
|
|
|
|
# prefix = '/api')
|
|
|
|
|
2023-03-03 06:02:00 +00:00
|
|
|
app.include_router(sessions.session_router, prefix="/api")
|
|
|
|
|
2023-03-15 05:15:53 +00:00
|
|
|
app.include_router(models.models_router, prefix="/api")
|
|
|
|
|
2023-05-21 13:03:52 +00:00
|
|
|
app.include_router(images.images_router, prefix="/api")
|
2022-12-01 05:33:20 +00:00
|
|
|
|
|
|
|
# Build a custom OpenAPI to include all outputs
|
|
|
|
# TODO: can outputs be included on metadata of invocation schemas somehow?
|
|
|
|
def custom_openapi():
|
|
|
|
if app.openapi_schema:
|
|
|
|
return app.openapi_schema
|
|
|
|
openapi_schema = get_openapi(
|
2023-03-03 06:02:00 +00:00
|
|
|
title=app.title,
|
|
|
|
description="An API for invoking AI image operations",
|
|
|
|
version="1.0.0",
|
|
|
|
routes=app.routes,
|
2022-12-01 05:33:20 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
# Add all outputs
|
|
|
|
all_invocations = BaseInvocation.get_invocations()
|
|
|
|
output_types = set()
|
|
|
|
output_type_titles = dict()
|
|
|
|
for invoker in all_invocations:
|
|
|
|
output_type = signature(invoker.invoke).return_annotation
|
|
|
|
output_types.add(output_type)
|
|
|
|
|
|
|
|
output_schemas = schema(output_types, ref_prefix="#/components/schemas/")
|
2023-03-03 06:02:00 +00:00
|
|
|
for schema_key, output_schema in output_schemas["definitions"].items():
|
2022-12-01 05:33:20 +00:00
|
|
|
openapi_schema["components"]["schemas"][schema_key] = output_schema
|
|
|
|
|
|
|
|
# TODO: note that we assume the schema_key here is the TYPE.__name__
|
|
|
|
# This could break in some cases, figure out a better way to do it
|
2023-03-03 06:02:00 +00:00
|
|
|
output_type_titles[schema_key] = output_schema["title"]
|
2022-12-01 05:33:20 +00:00
|
|
|
|
|
|
|
# Add a reference to the output type to additionalProperties of the invoker schema
|
|
|
|
for invoker in all_invocations:
|
|
|
|
invoker_name = invoker.__name__
|
|
|
|
output_type = signature(invoker.invoke).return_annotation
|
|
|
|
output_type_title = output_type_titles[output_type.__name__]
|
|
|
|
invoker_schema = openapi_schema["components"]["schemas"][invoker_name]
|
2023-03-03 06:02:00 +00:00
|
|
|
outputs_ref = {"$ref": f"#/components/schemas/{output_type_title}"}
|
|
|
|
|
2023-03-13 03:40:29 +00:00
|
|
|
invoker_schema["output"] = outputs_ref
|
2022-12-01 05:33:20 +00:00
|
|
|
|
|
|
|
app.openapi_schema = openapi_schema
|
|
|
|
return app.openapi_schema
|
|
|
|
|
2023-03-03 06:02:00 +00:00
|
|
|
|
2022-12-01 05:33:20 +00:00
|
|
|
app.openapi = custom_openapi
|
|
|
|
|
|
|
|
# Override API doc favicons
|
2023-05-22 20:48:17 +00:00
|
|
|
app.mount("/static", StaticFiles(directory=Path(web_dir.__path__[0], 'static/dream_web')), name="static")
|
2023-03-03 06:02:00 +00:00
|
|
|
|
2022-12-01 05:33:20 +00:00
|
|
|
@app.get("/docs", include_in_schema=False)
|
|
|
|
def overridden_swagger():
|
2023-03-03 06:02:00 +00:00
|
|
|
return get_swagger_ui_html(
|
2022-12-01 05:33:20 +00:00
|
|
|
openapi_url=app.openapi_url,
|
|
|
|
title=app.title,
|
2023-03-03 06:02:00 +00:00
|
|
|
swagger_favicon_url="/static/favicon.ico",
|
2022-12-01 05:33:20 +00:00
|
|
|
)
|
|
|
|
|
2023-03-03 06:02:00 +00:00
|
|
|
|
2022-12-01 05:33:20 +00:00
|
|
|
@app.get("/redoc", include_in_schema=False)
|
|
|
|
def overridden_redoc():
|
2023-03-03 06:02:00 +00:00
|
|
|
return get_redoc_html(
|
2022-12-01 05:33:20 +00:00
|
|
|
openapi_url=app.openapi_url,
|
|
|
|
title=app.title,
|
2023-03-03 06:02:00 +00:00
|
|
|
redoc_favicon_url="/static/favicon.ico",
|
2022-12-01 05:33:20 +00:00
|
|
|
)
|
|
|
|
|
2023-05-17 09:13:53 +00:00
|
|
|
|
2023-05-10 06:18:06 +00:00
|
|
|
# Must mount *after* the other routes else it borks em
|
2023-05-25 23:06:09 +00:00
|
|
|
app.mount("/",
|
|
|
|
StaticFiles(directory=Path(web_dir.__path__[0],"dist"),
|
|
|
|
html=True
|
|
|
|
), name="ui"
|
|
|
|
)
|
2023-03-03 06:02:00 +00:00
|
|
|
|
2022-12-01 05:33:20 +00:00
|
|
|
def invoke_api():
|
|
|
|
# Start our own event loop for eventing usage
|
|
|
|
loop = asyncio.new_event_loop()
|
2023-05-17 23:42:13 +00:00
|
|
|
config = uvicorn.Config(app=app, host=app_config.host, port=app_config.port, loop=loop)
|
2023-03-03 06:02:00 +00:00
|
|
|
# Use access_log to turn off logging
|
2022-12-01 05:33:20 +00:00
|
|
|
server = uvicorn.Server(config)
|
|
|
|
loop.run_until_complete(server.serve())
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
invoke_api()
|