Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions invokeai/app/api/routers/images.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,17 @@ async def delete_image(
# TODO: Does this need any exception handling at all?
pass

@images_router.post("/clear-intermediates", operation_id="clear_intermediates")
async def clear_intermediates() -> int:
"""Clears first 100 intermediates"""

try:
count_deleted = ApiDependencies.invoker.services.images.delete_many(is_intermediate=True)
return count_deleted
except Exception as e:
# TODO: Does this need any exception handling at all?
pass


@images_router.patch(
"/{image_name}",
Expand Down
16 changes: 10 additions & 6 deletions invokeai/app/services/image_record_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,8 @@ def update(
@abstractmethod
def get_many(
self,
offset: int = 0,
limit: int = 10,
offset: Optional[int] = None,
limit: Optional[int] = None,
image_origin: Optional[ResourceOrigin] = None,
categories: Optional[list[ImageCategory]] = None,
is_intermediate: Optional[bool] = None,
Expand Down Expand Up @@ -322,8 +322,8 @@ def update(

def get_many(
self,
offset: int = 0,
limit: int = 10,
offset: Optional[int] = None,
limit: Optional[int] = None,
image_origin: Optional[ResourceOrigin] = None,
categories: Optional[list[ImageCategory]] = None,
is_intermediate: Optional[bool] = None,
Expand Down Expand Up @@ -392,8 +392,12 @@ def get_many(
images_query += query_conditions + query_pagination + ";"
# Add all the parameters
images_params = query_params.copy()
images_params.append(limit)
images_params.append(offset)

if limit is not None:
images_params.append(limit)
if offset is not None:
images_params.append(offset)

# Build the list of images, deserializing each row
self._cursor.execute(images_query, images_params)
result = cast(list[sqlite3.Row], self._cursor.fetchall())
Expand Down
32 changes: 32 additions & 0 deletions invokeai/app/services/images.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,13 @@ def delete(self, image_name: str):
"""Deletes an image."""
pass

@abstractmethod
def delete_many(self, is_intermediate: bool) -> int:
"""Deletes many images."""
pass



@abstractmethod
def delete_images_on_board(self, board_id: str):
"""Deletes all images on a board."""
Expand Down Expand Up @@ -397,3 +404,28 @@ def delete_images_on_board(self, board_id: str):
except Exception as e:
self._services.logger.error("Problem deleting image records and files")
raise e

def delete_many(self, is_intermediate: bool):
try:
# only clears 100 at a time
images = self._services.image_records.get_many(offset=0, limit=100, is_intermediate=is_intermediate,)
count = len(images.items)
image_name_list = list(
map(
lambda r: r.image_name,
images.items,
)
)
for image_name in image_name_list:
self._services.image_files.delete(image_name)
self._services.image_records.delete_many(image_name_list)
return count
except ImageRecordDeleteException:
self._services.logger.error(f"Failed to delete image records")
raise
except ImageFileDeleteException:
self._services.logger.error(f"Failed to delete image files")
raise
except Exception as e:
self._services.logger.error("Problem deleting image records and files")
raise e
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { useCallback, useEffect, useState } from 'react';
import { StyledFlex } from './SettingsModal';
import { Heading, Text } from '@chakra-ui/react';
import IAIButton from '../../../../common/components/IAIButton';
import { useClearIntermediatesMutation } from '../../../../services/api/endpoints/images';
import { addToast } from '../../store/systemSlice';

export default function SettingsClearIntermediates() {
const dispatch = useAppDispatch();
const [isDisabled, setIsDisabled] = useState(false);

const [clearIntermediates, { isLoading: isLoadingClearIntermediates }] =
useClearIntermediatesMutation();

const handleClickClearIntermediates = useCallback(() => {
clearIntermediates({})
.unwrap()
.then((response) => {
dispatch(
addToast({
title:
response === 0
? `No intermediates to clear`
: `Successfully cleared ${response} intermediates`,
status: 'info',
})
);
if (response < 100) {
setIsDisabled(true);
}
});
}, [clearIntermediates, dispatch]);

return (
<StyledFlex>
<Heading size="sm">Clear Intermediates</Heading>
<IAIButton
colorScheme="error"
onClick={handleClickClearIntermediates}
isLoading={isLoadingClearIntermediates}
isDisabled={isDisabled}
>
{isDisabled ? 'Intermediates Cleared' : 'Clear 100 Intermediates'}
</IAIButton>
<Text>
Will permanently delete first 100 intermediates found on disk and in
database
</Text>
<Text>
Intermediate images are byproducts of generation, different from the
result images in the gallery. Purging intermediates will free disk
space. Your gallery images will not be deleted.
</Text>
</StyledFlex>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import {
import { useTranslation } from 'react-i18next';
import { LogLevelName } from 'roarr';
import SettingsSchedulers from './SettingsSchedulers';
import SettingsClearIntermediates from './SettingsClearIntermediates';

const selector = createSelector(
[systemSelector, uiSelector],
Expand Down Expand Up @@ -91,6 +92,7 @@ type ConfigOptions = {
shouldShowResetWebUiText: boolean;
shouldShowBetaLayout: boolean;
shouldShowAdvancedOptionsSettings: boolean;
shouldShowClearIntermediates: boolean;
};

type SettingsModalProps = {
Expand All @@ -109,6 +111,8 @@ const SettingsModal = ({ children, config }: SettingsModalProps) => {
const shouldShowResetWebUiText = config?.shouldShowResetWebUiText ?? true;
const shouldShowAdvancedOptionsSettings =
config?.shouldShowAdvancedOptionsSettings ?? true;
const shouldShowClearIntermediates =
config?.shouldShowClearIntermediates ?? true;

useEffect(() => {
if (!shouldShowDeveloperSettings) {
Expand Down Expand Up @@ -280,6 +284,8 @@ const SettingsModal = ({ children, config }: SettingsModalProps) => {
</StyledFlex>
)}

{shouldShowClearIntermediates && <SettingsClearIntermediates />}

<StyledFlex>
<Heading size="sm">{t('settings.resetWebUI')}</Heading>
<IAIButton colorScheme="error" onClick={handleClickResetWebUI}>
Expand Down Expand Up @@ -328,7 +334,7 @@ const SettingsModal = ({ children, config }: SettingsModalProps) => {

export default SettingsModal;

const StyledFlex = (props: PropsWithChildren) => {
export const StyledFlex = (props: PropsWithChildren) => {
return (
<Flex
sx={{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { ApiFullTagDescription, api } from '..';
import { components } from '../schema';
import { ImageDTO } from '../types';


/**
* This is an unsafe type; the object inside is not guaranteed to be valid.
*/
Expand Down Expand Up @@ -36,7 +37,10 @@ export const imagesApi = api.injectEndpoints({
},
keepUnusedDataFor: 86400, // 24 hours
}),
clearIntermediates: build.mutation({
query: () => ({ url: `images/clear-intermediates`, method: 'POST' }),
}),
}),
});

export const { useGetImageDTOQuery, useGetImageMetadataQuery } = imagesApi;
export const { useGetImageDTOQuery, useGetImageMetadataQuery, useClearIntermediatesMutation } = imagesApi;
29 changes: 25 additions & 4 deletions invokeai/frontend/web/src/services/api/schema.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,13 @@ export type paths = {
*/
patch: operations["update_image"];
};
"/api/v1/images/clear-intermediates": {
/**
* Clear Intermediates
* @description Clears first 100 intermediates
*/
post: operations["clear_intermediates"];
};
"/api/v1/images/{image_name}/metadata": {
/**
* Get Image Metadata
Expand Down Expand Up @@ -5299,17 +5306,17 @@ export type components = {
image?: components["schemas"]["ImageField"];
};
/**
* StableDiffusion2ModelFormat
* StableDiffusionXLModelFormat
* @description An enumeration.
* @enum {string}
*/
StableDiffusion2ModelFormat: "checkpoint" | "diffusers";
StableDiffusionXLModelFormat: "checkpoint" | "diffusers";
/**
* StableDiffusionXLModelFormat
* StableDiffusion2ModelFormat
* @description An enumeration.
* @enum {string}
*/
StableDiffusionXLModelFormat: "checkpoint" | "diffusers";
StableDiffusion2ModelFormat: "checkpoint" | "diffusers";
/**
* StableDiffusion1ModelFormat
* @description An enumeration.
Expand Down Expand Up @@ -6098,6 +6105,20 @@ export type operations = {
};
};
};
/**
* Clear Intermediates
* @description Clears first 100 intermediates
*/
clear_intermediates: {
responses: {
/** @description Successful Response */
200: {
content: {
"application/json": unknown;
};
};
};
};
/**
* Get Image Metadata
* @description Gets an image's metadata
Expand Down