Skip to content

Commit df1907e

Browse files
authored
Merge branch 'main' into install-script-python-version-error-prompt-fix
2 parents 8639794 + 22c337b commit df1907e

File tree

137 files changed

+4087
-1018
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

137 files changed

+4087
-1018
lines changed

invokeai/app/api/dependencies.py

+46-9
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,17 @@
22

33
from logging import Logger
44
import os
5+
from invokeai.app.services.board_image_record_storage import (
6+
SqliteBoardImageRecordStorage,
7+
)
8+
from invokeai.app.services.board_images import (
9+
BoardImagesService,
10+
BoardImagesServiceDependencies,
11+
)
12+
from invokeai.app.services.board_record_storage import SqliteBoardRecordStorage
13+
from invokeai.app.services.boards import BoardService, BoardServiceDependencies
514
from invokeai.app.services.image_record_storage import SqliteImageRecordStorage
6-
from invokeai.app.services.images import ImageService
15+
from invokeai.app.services.images import ImageService, ImageServiceDependencies
716
from invokeai.app.services.metadata import CoreMetadataService
817
from invokeai.app.services.resource_name import SimpleNameService
918
from invokeai.app.services.urls import LocalUrlService
@@ -57,7 +66,7 @@ def initialize(config, event_handler_id: int, logger: Logger = logger):
5766

5867
# TODO: build a file/path manager?
5968
db_location = config.db_path
60-
db_location.parent.mkdir(parents=True,exist_ok=True)
69+
db_location.parent.mkdir(parents=True, exist_ok=True)
6170

6271
graph_execution_manager = SqliteItemStorage[GraphExecutionState](
6372
filename=db_location, table_name="graph_executions"
@@ -72,21 +81,49 @@ def initialize(config, event_handler_id: int, logger: Logger = logger):
7281
DiskLatentsStorage(f"{output_folder}/latents")
7382
)
7483

84+
board_record_storage = SqliteBoardRecordStorage(db_location)
85+
board_image_record_storage = SqliteBoardImageRecordStorage(db_location)
86+
87+
boards = BoardService(
88+
services=BoardServiceDependencies(
89+
board_image_record_storage=board_image_record_storage,
90+
board_record_storage=board_record_storage,
91+
image_record_storage=image_record_storage,
92+
url=urls,
93+
logger=logger,
94+
)
95+
)
96+
97+
board_images = BoardImagesService(
98+
services=BoardImagesServiceDependencies(
99+
board_image_record_storage=board_image_record_storage,
100+
board_record_storage=board_record_storage,
101+
image_record_storage=image_record_storage,
102+
url=urls,
103+
logger=logger,
104+
)
105+
)
106+
75107
images = ImageService(
76-
image_record_storage=image_record_storage,
77-
image_file_storage=image_file_storage,
78-
metadata=metadata,
79-
url=urls,
80-
logger=logger,
81-
names=names,
82-
graph_execution_manager=graph_execution_manager,
108+
services=ImageServiceDependencies(
109+
board_image_record_storage=board_image_record_storage,
110+
image_record_storage=image_record_storage,
111+
image_file_storage=image_file_storage,
112+
metadata=metadata,
113+
url=urls,
114+
logger=logger,
115+
names=names,
116+
graph_execution_manager=graph_execution_manager,
117+
)
83118
)
84119

85120
services = InvocationServices(
86121
model_manager=ModelManagerService(config,logger),
87122
events=events,
88123
latents=latents,
89124
images=images,
125+
boards=boards,
126+
board_images=board_images,
90127
queue=MemoryInvocationQueue(),
91128
graph_library=SqliteItemStorage[LibraryGraph](
92129
filename=db_location, table_name="graphs"
+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
from fastapi import Body, HTTPException, Path, Query
2+
from fastapi.routing import APIRouter
3+
from invokeai.app.services.board_record_storage import BoardRecord, BoardChanges
4+
from invokeai.app.services.image_record_storage import OffsetPaginatedResults
5+
from invokeai.app.services.models.board_record import BoardDTO
6+
from invokeai.app.services.models.image_record import ImageDTO
7+
8+
from ..dependencies import ApiDependencies
9+
10+
board_images_router = APIRouter(prefix="/v1/board_images", tags=["boards"])
11+
12+
13+
@board_images_router.post(
14+
"/",
15+
operation_id="create_board_image",
16+
responses={
17+
201: {"description": "The image was added to a board successfully"},
18+
},
19+
status_code=201,
20+
)
21+
async def create_board_image(
22+
board_id: str = Body(description="The id of the board to add to"),
23+
image_name: str = Body(description="The name of the image to add"),
24+
):
25+
"""Creates a board_image"""
26+
try:
27+
result = ApiDependencies.invoker.services.board_images.add_image_to_board(board_id=board_id, image_name=image_name)
28+
return result
29+
except Exception as e:
30+
raise HTTPException(status_code=500, detail="Failed to add to board")
31+
32+
@board_images_router.delete(
33+
"/",
34+
operation_id="remove_board_image",
35+
responses={
36+
201: {"description": "The image was removed from the board successfully"},
37+
},
38+
status_code=201,
39+
)
40+
async def remove_board_image(
41+
board_id: str = Body(description="The id of the board"),
42+
image_name: str = Body(description="The name of the image to remove"),
43+
):
44+
"""Deletes a board_image"""
45+
try:
46+
result = ApiDependencies.invoker.services.board_images.remove_image_from_board(board_id=board_id, image_name=image_name)
47+
return result
48+
except Exception as e:
49+
raise HTTPException(status_code=500, detail="Failed to update board")
50+
51+
52+
53+
@board_images_router.get(
54+
"/{board_id}",
55+
operation_id="list_board_images",
56+
response_model=OffsetPaginatedResults[ImageDTO],
57+
)
58+
async def list_board_images(
59+
board_id: str = Path(description="The id of the board"),
60+
offset: int = Query(default=0, description="The page offset"),
61+
limit: int = Query(default=10, description="The number of boards per page"),
62+
) -> OffsetPaginatedResults[ImageDTO]:
63+
"""Gets a list of images for a board"""
64+
65+
results = ApiDependencies.invoker.services.board_images.get_images_for_board(
66+
board_id,
67+
)
68+
return results
69+

invokeai/app/api/routers/boards.py

+108
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
from typing import Optional, Union
2+
from fastapi import Body, HTTPException, Path, Query
3+
from fastapi.routing import APIRouter
4+
from invokeai.app.services.board_record_storage import BoardChanges
5+
from invokeai.app.services.image_record_storage import OffsetPaginatedResults
6+
from invokeai.app.services.models.board_record import BoardDTO
7+
8+
from ..dependencies import ApiDependencies
9+
10+
boards_router = APIRouter(prefix="/v1/boards", tags=["boards"])
11+
12+
13+
@boards_router.post(
14+
"/",
15+
operation_id="create_board",
16+
responses={
17+
201: {"description": "The board was created successfully"},
18+
},
19+
status_code=201,
20+
response_model=BoardDTO,
21+
)
22+
async def create_board(
23+
board_name: str = Query(description="The name of the board to create"),
24+
) -> BoardDTO:
25+
"""Creates a board"""
26+
try:
27+
result = ApiDependencies.invoker.services.boards.create(board_name=board_name)
28+
return result
29+
except Exception as e:
30+
raise HTTPException(status_code=500, detail="Failed to create board")
31+
32+
33+
@boards_router.get("/{board_id}", operation_id="get_board", response_model=BoardDTO)
34+
async def get_board(
35+
board_id: str = Path(description="The id of board to get"),
36+
) -> BoardDTO:
37+
"""Gets a board"""
38+
39+
try:
40+
result = ApiDependencies.invoker.services.boards.get_dto(board_id=board_id)
41+
return result
42+
except Exception as e:
43+
raise HTTPException(status_code=404, detail="Board not found")
44+
45+
46+
@boards_router.patch(
47+
"/{board_id}",
48+
operation_id="update_board",
49+
responses={
50+
201: {
51+
"description": "The board was updated successfully",
52+
},
53+
},
54+
status_code=201,
55+
response_model=BoardDTO,
56+
)
57+
async def update_board(
58+
board_id: str = Path(description="The id of board to update"),
59+
changes: BoardChanges = Body(description="The changes to apply to the board"),
60+
) -> BoardDTO:
61+
"""Updates a board"""
62+
try:
63+
result = ApiDependencies.invoker.services.boards.update(
64+
board_id=board_id, changes=changes
65+
)
66+
return result
67+
except Exception as e:
68+
raise HTTPException(status_code=500, detail="Failed to update board")
69+
70+
71+
@boards_router.delete("/{board_id}", operation_id="delete_board")
72+
async def delete_board(
73+
board_id: str = Path(description="The id of board to delete"),
74+
) -> None:
75+
"""Deletes a board"""
76+
77+
try:
78+
ApiDependencies.invoker.services.boards.delete(board_id=board_id)
79+
except Exception as e:
80+
# TODO: Does this need any exception handling at all?
81+
pass
82+
83+
84+
@boards_router.get(
85+
"/",
86+
operation_id="list_boards",
87+
response_model=Union[OffsetPaginatedResults[BoardDTO], list[BoardDTO]],
88+
)
89+
async def list_boards(
90+
all: Optional[bool] = Query(default=None, description="Whether to list all boards"),
91+
offset: Optional[int] = Query(default=None, description="The page offset"),
92+
limit: Optional[int] = Query(
93+
default=None, description="The number of boards per page"
94+
),
95+
) -> Union[OffsetPaginatedResults[BoardDTO], list[BoardDTO]]:
96+
"""Gets a list of boards"""
97+
if all:
98+
return ApiDependencies.invoker.services.boards.get_all()
99+
elif offset is not None and limit is not None:
100+
return ApiDependencies.invoker.services.boards.get_many(
101+
offset,
102+
limit,
103+
)
104+
else:
105+
raise HTTPException(
106+
status_code=400,
107+
detail="Invalid request: Must provide either 'all' or both 'offset' and 'limit'",
108+
)

invokeai/app/api/routers/images.py

+4
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,9 @@ async def list_images_with_metadata(
221221
is_intermediate: Optional[bool] = Query(
222222
default=None, description="Whether to list intermediate images"
223223
),
224+
board_id: Optional[str] = Query(
225+
default=None, description="The board id to filter by"
226+
),
224227
offset: int = Query(default=0, description="The page offset"),
225228
limit: int = Query(default=10, description="The number of images per page"),
226229
) -> OffsetPaginatedResults[ImageDTO]:
@@ -232,6 +235,7 @@ async def list_images_with_metadata(
232235
image_origin,
233236
categories,
234237
is_intermediate,
238+
board_id,
235239
)
236240

237241
return image_dtos

invokeai/app/api/routers/models.py

+5-6
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
from pydantic import BaseModel, Field, parse_obj_as
88
from ..dependencies import ApiDependencies
99
from invokeai.backend import BaseModelType, ModelType
10-
from invokeai.backend.model_management.models import get_all_model_configs
11-
MODEL_CONFIGS = Union[tuple(get_all_model_configs())]
10+
from invokeai.backend.model_management.models import OPENAPI_MODEL_CONFIGS
11+
MODEL_CONFIGS = Union[tuple(OPENAPI_MODEL_CONFIGS)]
1212

1313
models_router = APIRouter(prefix="/v1/models", tags=["models"])
1414

@@ -62,8 +62,7 @@ class ConvertedModelResponse(BaseModel):
6262
info: DiffusersModelInfo = Field(description="The converted model info")
6363

6464
class ModelsList(BaseModel):
65-
models: Dict[BaseModelType, Dict[ModelType, Dict[str, MODEL_CONFIGS]]] # TODO: debug/discuss with frontend
66-
#models: dict[SDModelType, dict[str, Annotated[Union[(DiffusersModelInfo,CkptModelInfo,SafetensorsModelInfo)], Field(discriminator="format")]]]
65+
models: list[MODEL_CONFIGS]
6766

6867

6968
@models_router.get(
@@ -72,10 +71,10 @@ class ModelsList(BaseModel):
7271
responses={200: {"model": ModelsList }},
7372
)
7473
async def list_models(
75-
base_model: BaseModelType = Query(
74+
base_model: Optional[BaseModelType] = Query(
7675
default=None, description="Base model"
7776
),
78-
model_type: ModelType = Query(
77+
model_type: Optional[ModelType] = Query(
7978
default=None, description="The type of model to get"
8079
),
8180
) -> ModelsList:

invokeai/app/api_app.py

+21-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
import invokeai.frontend.web as web_dir
2525

2626
from .api.dependencies import ApiDependencies
27-
from .api.routers import sessions, models, images
27+
from .api.routers import sessions, models, images, boards, board_images
2828
from .api.sockets import SocketIO
2929
from .invocations.baseinvocation import BaseInvocation
3030

@@ -78,6 +78,10 @@ async def shutdown_event():
7878

7979
app.include_router(images.images_router, prefix="/api")
8080

81+
app.include_router(boards.boards_router, prefix="/api")
82+
83+
app.include_router(board_images.board_images_router, prefix="/api")
84+
8185
# Build a custom OpenAPI to include all outputs
8286
# TODO: can outputs be included on metadata of invocation schemas somehow?
8387
def custom_openapi():
@@ -116,6 +120,22 @@ def custom_openapi():
116120

117121
invoker_schema["output"] = outputs_ref
118122

123+
from invokeai.backend.model_management.models import get_model_config_enums
124+
for model_config_format_enum in set(get_model_config_enums()):
125+
name = model_config_format_enum.__qualname__
126+
127+
if name in openapi_schema["components"]["schemas"]:
128+
# print(f"Config with name {name} already defined")
129+
continue
130+
131+
# "BaseModelType":{"title":"BaseModelType","description":"An enumeration.","enum":["sd-1","sd-2"],"type":"string"}
132+
openapi_schema["components"]["schemas"][name] = dict(
133+
title=name,
134+
description="An enumeration.",
135+
type="string",
136+
enum=list(v.value for v in model_config_format_enum),
137+
)
138+
119139
app.openapi_schema = openapi_schema
120140
return app.openapi_schema
121141

0 commit comments

Comments
 (0)