Skip to content

Commit 0a0b714

Browse files
authored
Merge branch 'main' into feat/execution-stats
2 parents 06742fa + bf94412 commit 0a0b714

File tree

120 files changed

+2494
-2181
lines changed

Some content is hidden

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

120 files changed

+2494
-2181
lines changed
Lines changed: 75 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,30 @@
1-
from fastapi import Body, HTTPException, Path, Query
1+
from fastapi import Body, HTTPException
22
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
3+
from pydantic import BaseModel, Field
74

85
from ..dependencies import ApiDependencies
96

107
board_images_router = APIRouter(prefix="/v1/board_images", tags=["boards"])
118

129

10+
class AddImagesToBoardResult(BaseModel):
11+
board_id: str = Field(description="The id of the board the images were added to")
12+
added_image_names: list[str] = Field(description="The image names that were added to the board")
13+
14+
15+
class RemoveImagesFromBoardResult(BaseModel):
16+
removed_image_names: list[str] = Field(description="The image names that were removed from their board")
17+
18+
1319
@board_images_router.post(
1420
"/",
15-
operation_id="create_board_image",
21+
operation_id="add_image_to_board",
1622
responses={
1723
201: {"description": "The image was added to a board successfully"},
1824
},
1925
status_code=201,
2026
)
21-
async def create_board_image(
27+
async def add_image_to_board(
2228
board_id: str = Body(description="The id of the board to add to"),
2329
image_name: str = Body(description="The name of the image to add"),
2430
):
@@ -29,26 +35,78 @@ async def create_board_image(
2935
)
3036
return result
3137
except Exception as e:
32-
raise HTTPException(status_code=500, detail="Failed to add to board")
38+
raise HTTPException(status_code=500, detail="Failed to add image to board")
3339

3440

3541
@board_images_router.delete(
3642
"/",
37-
operation_id="remove_board_image",
43+
operation_id="remove_image_from_board",
3844
responses={
3945
201: {"description": "The image was removed from the board successfully"},
4046
},
4147
status_code=201,
4248
)
43-
async def remove_board_image(
44-
board_id: str = Body(description="The id of the board"),
45-
image_name: str = Body(description="The name of the image to remove"),
49+
async def remove_image_from_board(
50+
image_name: str = Body(description="The name of the image to remove", embed=True),
4651
):
47-
"""Deletes a board_image"""
52+
"""Removes an image from its board, if it had one"""
4853
try:
49-
result = ApiDependencies.invoker.services.board_images.remove_image_from_board(
50-
board_id=board_id, image_name=image_name
51-
)
54+
result = ApiDependencies.invoker.services.board_images.remove_image_from_board(image_name=image_name)
5255
return result
5356
except Exception as e:
54-
raise HTTPException(status_code=500, detail="Failed to update board")
57+
raise HTTPException(status_code=500, detail="Failed to remove image from board")
58+
59+
60+
@board_images_router.post(
61+
"/batch",
62+
operation_id="add_images_to_board",
63+
responses={
64+
201: {"description": "Images were added to board successfully"},
65+
},
66+
status_code=201,
67+
response_model=AddImagesToBoardResult,
68+
)
69+
async def add_images_to_board(
70+
board_id: str = Body(description="The id of the board to add to"),
71+
image_names: list[str] = Body(description="The names of the images to add", embed=True),
72+
) -> AddImagesToBoardResult:
73+
"""Adds a list of images to a board"""
74+
try:
75+
added_image_names: list[str] = []
76+
for image_name in image_names:
77+
try:
78+
ApiDependencies.invoker.services.board_images.add_image_to_board(
79+
board_id=board_id, image_name=image_name
80+
)
81+
added_image_names.append(image_name)
82+
except:
83+
pass
84+
return AddImagesToBoardResult(board_id=board_id, added_image_names=added_image_names)
85+
except Exception as e:
86+
raise HTTPException(status_code=500, detail="Failed to add images to board")
87+
88+
89+
@board_images_router.post(
90+
"/batch/delete",
91+
operation_id="remove_images_from_board",
92+
responses={
93+
201: {"description": "Images were removed from board successfully"},
94+
},
95+
status_code=201,
96+
response_model=RemoveImagesFromBoardResult,
97+
)
98+
async def remove_images_from_board(
99+
image_names: list[str] = Body(description="The names of the images to remove", embed=True),
100+
) -> RemoveImagesFromBoardResult:
101+
"""Removes a list of images from their board, if they had one"""
102+
try:
103+
removed_image_names: list[str] = []
104+
for image_name in image_names:
105+
try:
106+
ApiDependencies.invoker.services.board_images.remove_image_from_board(image_name=image_name)
107+
removed_image_names.append(image_name)
108+
except:
109+
pass
110+
return RemoveImagesFromBoardResult(removed_image_names=removed_image_names)
111+
except Exception as e:
112+
raise HTTPException(status_code=500, detail="Failed to remove images from board")

invokeai/app/api/routers/images.py

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from fastapi.responses import FileResponse
66
from fastapi.routing import APIRouter
77
from PIL import Image
8+
from pydantic import BaseModel, Field
89

910
from invokeai.app.invocations.metadata import ImageMetadata
1011
from invokeai.app.models.image import ImageCategory, ResourceOrigin
@@ -25,7 +26,7 @@
2526

2627

2728
@images_router.post(
28-
"/",
29+
"/upload",
2930
operation_id="upload_image",
3031
responses={
3132
201: {"description": "The image was uploaded successfully"},
@@ -77,7 +78,7 @@ async def upload_image(
7778
raise HTTPException(status_code=500, detail="Failed to create image")
7879

7980

80-
@images_router.delete("/{image_name}", operation_id="delete_image")
81+
@images_router.delete("/i/{image_name}", operation_id="delete_image")
8182
async def delete_image(
8283
image_name: str = Path(description="The name of the image to delete"),
8384
) -> None:
@@ -103,7 +104,7 @@ async def clear_intermediates() -> int:
103104

104105

105106
@images_router.patch(
106-
"/{image_name}",
107+
"/i/{image_name}",
107108
operation_id="update_image",
108109
response_model=ImageDTO,
109110
)
@@ -120,7 +121,7 @@ async def update_image(
120121

121122

122123
@images_router.get(
123-
"/{image_name}",
124+
"/i/{image_name}",
124125
operation_id="get_image_dto",
125126
response_model=ImageDTO,
126127
)
@@ -136,7 +137,7 @@ async def get_image_dto(
136137

137138

138139
@images_router.get(
139-
"/{image_name}/metadata",
140+
"/i/{image_name}/metadata",
140141
operation_id="get_image_metadata",
141142
response_model=ImageMetadata,
142143
)
@@ -152,7 +153,7 @@ async def get_image_metadata(
152153

153154

154155
@images_router.get(
155-
"/{image_name}/full",
156+
"/i/{image_name}/full",
156157
operation_id="get_image_full",
157158
response_class=Response,
158159
responses={
@@ -187,7 +188,7 @@ async def get_image_full(
187188

188189

189190
@images_router.get(
190-
"/{image_name}/thumbnail",
191+
"/i/{image_name}/thumbnail",
191192
operation_id="get_image_thumbnail",
192193
response_class=Response,
193194
responses={
@@ -216,7 +217,7 @@ async def get_image_thumbnail(
216217

217218

218219
@images_router.get(
219-
"/{image_name}/urls",
220+
"/i/{image_name}/urls",
220221
operation_id="get_image_urls",
221222
response_model=ImageUrlsDTO,
222223
)
@@ -265,3 +266,24 @@ async def list_image_dtos(
265266
)
266267

267268
return image_dtos
269+
270+
271+
class DeleteImagesFromListResult(BaseModel):
272+
deleted_images: list[str]
273+
274+
275+
@images_router.post("/delete", operation_id="delete_images_from_list", response_model=DeleteImagesFromListResult)
276+
async def delete_images_from_list(
277+
image_names: list[str] = Body(description="The list of names of images to delete", embed=True),
278+
) -> DeleteImagesFromListResult:
279+
try:
280+
deleted_images: list[str] = []
281+
for image_name in image_names:
282+
try:
283+
ApiDependencies.invoker.services.images.delete(image_name)
284+
deleted_images.append(image_name)
285+
except:
286+
pass
287+
return DeleteImagesFromListResult(deleted_images=deleted_images)
288+
except Exception as e:
289+
raise HTTPException(status_code=500, detail="Failed to delete images")

invokeai/app/invocations/metadata.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from typing import Literal, Optional, Union
22

3-
from pydantic import BaseModel, Field
3+
from pydantic import Field
44

55
from invokeai.app.invocations.baseinvocation import (
66
BaseInvocation,
@@ -10,16 +10,17 @@
1010
)
1111
from invokeai.app.invocations.controlnet_image_processors import ControlField
1212
from invokeai.app.invocations.model import LoRAModelField, MainModelField, VAEModelField
13+
from invokeai.app.util.model_exclude_null import BaseModelExcludeNull
1314

1415

15-
class LoRAMetadataField(BaseModel):
16+
class LoRAMetadataField(BaseModelExcludeNull):
1617
"""LoRA metadata for an image generated in InvokeAI."""
1718

1819
lora: LoRAModelField = Field(description="The LoRA model")
1920
weight: float = Field(description="The weight of the LoRA model")
2021

2122

22-
class CoreMetadata(BaseModel):
23+
class CoreMetadata(BaseModelExcludeNull):
2324
"""Core generation metadata for an image generated in InvokeAI."""
2425

2526
generation_mode: str = Field(
@@ -70,7 +71,7 @@ class CoreMetadata(BaseModel):
7071
refiner_start: Union[float, None] = Field(default=None, description="The start value used for refiner denoising")
7172

7273

73-
class ImageMetadata(BaseModel):
74+
class ImageMetadata(BaseModelExcludeNull):
7475
"""An image's generation metadata"""
7576

7677
metadata: Optional[dict] = Field(

invokeai/app/services/board_image_record_storage.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ def add_image_to_board(
2525
@abstractmethod
2626
def remove_image_from_board(
2727
self,
28-
board_id: str,
2928
image_name: str,
3029
) -> None:
3130
"""Removes an image from a board."""
@@ -154,17 +153,16 @@ def add_image_to_board(
154153

155154
def remove_image_from_board(
156155
self,
157-
board_id: str,
158156
image_name: str,
159157
) -> None:
160158
try:
161159
self._lock.acquire()
162160
self._cursor.execute(
163161
"""--sql
164162
DELETE FROM board_images
165-
WHERE board_id = ? AND image_name = ?;
163+
WHERE image_name = ?;
166164
""",
167-
(board_id, image_name),
165+
(image_name,),
168166
)
169167
self._conn.commit()
170168
except sqlite3.Error as e:

invokeai/app/services/board_images.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ def add_image_to_board(
3131
@abstractmethod
3232
def remove_image_from_board(
3333
self,
34-
board_id: str,
3534
image_name: str,
3635
) -> None:
3736
"""Removes an image from a board."""
@@ -93,10 +92,9 @@ def add_image_to_board(
9392

9493
def remove_image_from_board(
9594
self,
96-
board_id: str,
9795
image_name: str,
9896
) -> None:
99-
self._services.board_image_records.remove_image_from_board(board_id, image_name)
97+
self._services.board_image_records.remove_image_from_board(image_name)
10098

10199
def get_all_board_image_names_for_board(
102100
self,
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from pydantic import Field
2+
3+
from invokeai.app.util.model_exclude_null import BaseModelExcludeNull
4+
5+
6+
class BoardImage(BaseModelExcludeNull):
7+
board_id: str = Field(description="The id of the board")
8+
image_name: str = Field(description="The name of the image")

invokeai/app/services/models/board_record.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
from typing import Optional, Union
22
from datetime import datetime
3-
from pydantic import BaseModel, Extra, Field, StrictBool, StrictStr
3+
from pydantic import Field
44
from invokeai.app.util.misc import get_iso_timestamp
5+
from invokeai.app.util.model_exclude_null import BaseModelExcludeNull
56

67

7-
class BoardRecord(BaseModel):
8+
class BoardRecord(BaseModelExcludeNull):
89
"""Deserialized board record."""
910

1011
board_id: str = Field(description="The unique ID of the board.")

invokeai/app/services/models/image_record.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import datetime
22
from typing import Optional, Union
33

4-
from pydantic import BaseModel, Extra, Field, StrictBool, StrictStr
4+
from pydantic import Extra, Field, StrictBool, StrictStr
55

66
from invokeai.app.models.image import ImageCategory, ResourceOrigin
77
from invokeai.app.util.misc import get_iso_timestamp
8+
from invokeai.app.util.model_exclude_null import BaseModelExcludeNull
89

910

10-
class ImageRecord(BaseModel):
11+
class ImageRecord(BaseModelExcludeNull):
1112
"""Deserialized image record without metadata."""
1213

1314
image_name: str = Field(description="The unique name of the image.")
@@ -40,7 +41,7 @@ class ImageRecord(BaseModel):
4041
"""The node ID that generated this image, if it is a generated image."""
4142

4243

43-
class ImageRecordChanges(BaseModel, extra=Extra.forbid):
44+
class ImageRecordChanges(BaseModelExcludeNull, extra=Extra.forbid):
4445
"""A set of changes to apply to an image record.
4546
4647
Only limited changes are valid:
@@ -60,7 +61,7 @@ class ImageRecordChanges(BaseModel, extra=Extra.forbid):
6061
"""The image's new `is_intermediate` flag."""
6162

6263

63-
class ImageUrlsDTO(BaseModel):
64+
class ImageUrlsDTO(BaseModelExcludeNull):
6465
"""The URLs for an image and its thumbnail."""
6566

6667
image_name: str = Field(description="The unique name of the image.")
@@ -76,11 +77,15 @@ class ImageDTO(ImageRecord, ImageUrlsDTO):
7677

7778
board_id: Optional[str] = Field(description="The id of the board the image belongs to, if one exists.")
7879
"""The id of the board the image belongs to, if one exists."""
80+
7981
pass
8082

8183

8284
def image_record_to_dto(
83-
image_record: ImageRecord, image_url: str, thumbnail_url: str, board_id: Optional[str]
85+
image_record: ImageRecord,
86+
image_url: str,
87+
thumbnail_url: str,
88+
board_id: Optional[str],
8489
) -> ImageDTO:
8590
"""Converts an image record to an image DTO."""
8691
return ImageDTO(

0 commit comments

Comments
 (0)