mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(nodes): add enable, disable, status to invocation cache
- New routes to clear, enable, disable and get the status of the cache - Status includes hits, misses, size, max size, enabled - Add client cache queries and mutations, abstracted into hooks - Add invocation cache status area (next to queue status) w/ buttons
This commit is contained in:
parent
aa82f9360c
commit
7ac99d6bc3
@ -7,6 +7,7 @@ from fastapi.routing import APIRouter
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from invokeai.app.invocations.upscale import ESRGAN_MODELS
|
||||
from invokeai.app.services.invocation_cache.invocation_cache_common import InvocationCacheStatus
|
||||
from invokeai.backend.image_util.invisible_watermark import InvisibleWatermark
|
||||
from invokeai.backend.image_util.patchmatch import PatchMatch
|
||||
from invokeai.backend.image_util.safety_checker import SafetyChecker
|
||||
@ -113,3 +114,33 @@ async def set_log_level(
|
||||
async def clear_invocation_cache() -> None:
|
||||
"""Clears the invocation cache"""
|
||||
ApiDependencies.invoker.services.invocation_cache.clear()
|
||||
|
||||
|
||||
@app_router.put(
|
||||
"/invocation_cache/enable",
|
||||
operation_id="enable_invocation_cache",
|
||||
responses={200: {"description": "The operation was successful"}},
|
||||
)
|
||||
async def enable_invocation_cache() -> None:
|
||||
"""Clears the invocation cache"""
|
||||
ApiDependencies.invoker.services.invocation_cache.enable()
|
||||
|
||||
|
||||
@app_router.put(
|
||||
"/invocation_cache/disable",
|
||||
operation_id="disable_invocation_cache",
|
||||
responses={200: {"description": "The operation was successful"}},
|
||||
)
|
||||
async def disable_invocation_cache() -> None:
|
||||
"""Clears the invocation cache"""
|
||||
ApiDependencies.invoker.services.invocation_cache.disable()
|
||||
|
||||
|
||||
@app_router.get(
|
||||
"/invocation_cache/status",
|
||||
operation_id="get_invocation_cache_status",
|
||||
responses={200: {"model": InvocationCacheStatus}},
|
||||
)
|
||||
async def get_invocation_cache_status() -> InvocationCacheStatus:
|
||||
"""Clears the invocation cache"""
|
||||
return ApiDependencies.invoker.services.invocation_cache.get_status()
|
||||
|
@ -2,6 +2,7 @@ from abc import ABC, abstractmethod
|
||||
from typing import Optional, Union
|
||||
|
||||
from invokeai.app.invocations.baseinvocation import BaseInvocation, BaseInvocationOutput
|
||||
from invokeai.app.services.invocation_cache.invocation_cache_common import InvocationCacheStatus
|
||||
|
||||
|
||||
class InvocationCacheBase(ABC):
|
||||
@ -32,7 +33,7 @@ class InvocationCacheBase(ABC):
|
||||
|
||||
@abstractmethod
|
||||
def delete(self, key: Union[int, str]) -> None:
|
||||
"""Deleteds an invocation output from the cache"""
|
||||
"""Deletes an invocation output from the cache"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
@ -44,3 +45,18 @@ class InvocationCacheBase(ABC):
|
||||
def create_key(self, invocation: BaseInvocation) -> int:
|
||||
"""Gets the key for the invocation's cache item"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def disable(self) -> None:
|
||||
"""Disables the cache, overriding the max cache size"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def enable(self) -> None:
|
||||
"""Enables the cache, letting the the max cache size take effect"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_status(self) -> InvocationCacheStatus:
|
||||
"""Returns the status of the cache"""
|
||||
pass
|
||||
|
@ -0,0 +1,9 @@
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class InvocationCacheStatus(BaseModel):
|
||||
size: int = Field(description="The current size of the invocation cache")
|
||||
hits: int = Field(description="The number of cache hits")
|
||||
misses: int = Field(description="The number of cache misses")
|
||||
enabled: bool = Field(description="Whether the invocation cache is enabled")
|
||||
max_size: int = Field(description="The maximum size of the invocation cache")
|
@ -3,18 +3,25 @@ from typing import Optional, Union
|
||||
|
||||
from invokeai.app.invocations.baseinvocation import BaseInvocation, BaseInvocationOutput
|
||||
from invokeai.app.services.invocation_cache.invocation_cache_base import InvocationCacheBase
|
||||
from invokeai.app.services.invocation_cache.invocation_cache_common import InvocationCacheStatus
|
||||
from invokeai.app.services.invoker import Invoker
|
||||
|
||||
|
||||
class MemoryInvocationCache(InvocationCacheBase):
|
||||
__cache: dict[Union[int, str], tuple[BaseInvocationOutput, str]]
|
||||
__max_cache_size: int
|
||||
__disabled: bool
|
||||
__hits: int
|
||||
__misses: int
|
||||
__cache_ids: Queue
|
||||
__invoker: Invoker
|
||||
|
||||
def __init__(self, max_cache_size: int = 0) -> None:
|
||||
self.__cache = dict()
|
||||
self.__max_cache_size = max_cache_size
|
||||
self.__disabled = False
|
||||
self.__hits = 0
|
||||
self.__misses = 0
|
||||
self.__cache_ids = Queue()
|
||||
|
||||
def start(self, invoker: Invoker) -> None:
|
||||
@ -25,15 +32,17 @@ class MemoryInvocationCache(InvocationCacheBase):
|
||||
self.__invoker.services.latents.on_deleted(self._delete_by_match)
|
||||
|
||||
def get(self, key: Union[int, str]) -> Optional[BaseInvocationOutput]:
|
||||
if self.__max_cache_size == 0:
|
||||
if self.__max_cache_size == 0 or self.__disabled:
|
||||
return
|
||||
|
||||
item = self.__cache.get(key, None)
|
||||
if item is not None:
|
||||
self.__hits += 1
|
||||
return item[0]
|
||||
self.__misses += 1
|
||||
|
||||
def save(self, key: Union[int, str], invocation_output: BaseInvocationOutput) -> None:
|
||||
if self.__max_cache_size == 0:
|
||||
if self.__max_cache_size == 0 or self.__disabled:
|
||||
return
|
||||
|
||||
if key not in self.__cache:
|
||||
@ -47,24 +56,41 @@ class MemoryInvocationCache(InvocationCacheBase):
|
||||
pass
|
||||
|
||||
def delete(self, key: Union[int, str]) -> None:
|
||||
if self.__max_cache_size == 0:
|
||||
if self.__max_cache_size == 0 or self.__disabled:
|
||||
return
|
||||
|
||||
if key in self.__cache:
|
||||
del self.__cache[key]
|
||||
|
||||
def clear(self, *args, **kwargs) -> None:
|
||||
if self.__max_cache_size == 0:
|
||||
if self.__max_cache_size == 0 or self.__disabled:
|
||||
return
|
||||
|
||||
self.__cache.clear()
|
||||
self.__cache_ids = Queue()
|
||||
self.__misses = 0
|
||||
self.__hits = 0
|
||||
|
||||
def create_key(self, invocation: BaseInvocation) -> int:
|
||||
return hash(invocation.json(exclude={"id"}))
|
||||
|
||||
def disable(self) -> None:
|
||||
self.__disabled = True
|
||||
|
||||
def enable(self) -> None:
|
||||
self.__disabled = False
|
||||
|
||||
def get_status(self) -> InvocationCacheStatus:
|
||||
return InvocationCacheStatus(
|
||||
hits=self.__hits,
|
||||
misses=self.__misses,
|
||||
enabled=not self.__disabled,
|
||||
size=len(self.__cache),
|
||||
max_size=self.__max_cache_size,
|
||||
)
|
||||
|
||||
def _delete_by_match(self, to_match: str) -> None:
|
||||
if self.__max_cache_size == 0:
|
||||
if self.__max_cache_size == 0 or self.__disabled:
|
||||
return
|
||||
|
||||
keys_to_delete = set()
|
||||
|
@ -162,15 +162,15 @@ class SessionQueueItemWithoutGraph(BaseModel):
|
||||
session_id: str = Field(
|
||||
description="The ID of the session associated with this queue item. The session doesn't exist in graph_executions until the queue item is executed."
|
||||
)
|
||||
field_values: Optional[list[NodeFieldValue]] = Field(
|
||||
default=None, description="The field values that were used for this queue item"
|
||||
)
|
||||
queue_id: str = Field(description="The id of the queue with which this item is associated")
|
||||
error: Optional[str] = Field(default=None, description="The error message if this queue item errored")
|
||||
created_at: Union[datetime.datetime, str] = Field(description="When this queue item was created")
|
||||
updated_at: Union[datetime.datetime, str] = Field(description="When this queue item was updated")
|
||||
started_at: Optional[Union[datetime.datetime, str]] = Field(description="When this queue item was started")
|
||||
completed_at: Optional[Union[datetime.datetime, str]] = Field(description="When this queue item was completed")
|
||||
queue_id: str = Field(description="The id of the queue with which this item is associated")
|
||||
field_values: Optional[list[NodeFieldValue]] = Field(
|
||||
default=None, description="The field values that were used for this queue item"
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, queue_item_dict: dict) -> "SessionQueueItemDTO":
|
||||
|
@ -264,6 +264,22 @@
|
||||
"graphQueued": "Graph queued",
|
||||
"graphFailedToQueue": "Failed to queue graph"
|
||||
},
|
||||
"invocationCache": {
|
||||
"invocationCache": "Invocation Cache",
|
||||
"cacheSize": "Cache Size",
|
||||
"maxCacheSize": "Max Cache Size",
|
||||
"hits": "Cache Hits",
|
||||
"misses": "Cache Misses",
|
||||
"clear": "Clear",
|
||||
"clearSucceeded": "Invocation Cache Cleared",
|
||||
"clearFailed": "Problem Clearing Invocation Cache",
|
||||
"enable": "Enable",
|
||||
"enableSucceeded": "Invocation Cache Enabled",
|
||||
"enableFailed": "Problem Enabling Invocation Cache",
|
||||
"disable": "Disable",
|
||||
"disableSucceeded": "Invocation Cache Disabled",
|
||||
"disableFailed": "Problem Disabling Invocation Cache"
|
||||
},
|
||||
"gallery": {
|
||||
"allImagesLoaded": "All Images Loaded",
|
||||
"assets": "Assets",
|
||||
|
@ -21,7 +21,8 @@ export type AppFeature =
|
||||
| 'multiselect'
|
||||
| 'pauseQueue'
|
||||
| 'resumeQueue'
|
||||
| 'prependQueue';
|
||||
| 'prependQueue'
|
||||
| 'invocationCache';
|
||||
|
||||
/**
|
||||
* A disable-able Stable Diffusion feature
|
||||
|
@ -0,0 +1,22 @@
|
||||
import IAIButton from 'common/components/IAIButton';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useClearInvocationCache } from '../hooks/useClearInvocationCache';
|
||||
|
||||
const ClearInvocationCacheButton = () => {
|
||||
const { t } = useTranslation();
|
||||
const { clearInvocationCache, isDisabled, isLoading } =
|
||||
useClearInvocationCache();
|
||||
|
||||
return (
|
||||
<IAIButton
|
||||
isDisabled={isDisabled}
|
||||
isLoading={isLoading}
|
||||
onClick={clearInvocationCache}
|
||||
>
|
||||
{t('invocationCache.clear')}
|
||||
</IAIButton>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(ClearInvocationCacheButton);
|
@ -0,0 +1,45 @@
|
||||
import { ButtonGroup } from '@chakra-ui/react';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useGetInvocationCacheStatusQuery } from 'services/api/endpoints/appInfo';
|
||||
import ClearInvocationCacheButton from './ClearInvocationCacheButton';
|
||||
import ToggleInvocationCacheButton from './ToggleInvocationCacheButton';
|
||||
import StatusStatGroup from './common/StatusStatGroup';
|
||||
import StatusStatItem from './common/StatusStatItem';
|
||||
|
||||
const InvocationCacheStatus = () => {
|
||||
const { data: cacheStatus } = useGetInvocationCacheStatusQuery(undefined, {
|
||||
pollingInterval: 5000,
|
||||
});
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<StatusStatGroup>
|
||||
<StatusStatItem
|
||||
isDisabled={!cacheStatus?.enabled}
|
||||
label={t('invocationCache.cacheSize')}
|
||||
value={cacheStatus?.size ?? 0}
|
||||
/>
|
||||
<StatusStatItem
|
||||
isDisabled={!cacheStatus?.enabled}
|
||||
label={t('invocationCache.hits')}
|
||||
value={cacheStatus?.hits ?? 0}
|
||||
/>
|
||||
<StatusStatItem
|
||||
isDisabled={!cacheStatus?.enabled}
|
||||
label={t('invocationCache.misses')}
|
||||
value={cacheStatus?.misses ?? 0}
|
||||
/>
|
||||
<StatusStatItem
|
||||
isDisabled={!cacheStatus?.enabled}
|
||||
label={t('invocationCache.maxCacheSize')}
|
||||
value={cacheStatus?.max_size ?? 0}
|
||||
/>
|
||||
<ButtonGroup w={24} orientation="vertical" size="xs">
|
||||
<ClearInvocationCacheButton />
|
||||
<ToggleInvocationCacheButton />
|
||||
</ButtonGroup>
|
||||
</StatusStatGroup>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(InvocationCacheStatus);
|
@ -1,38 +1,39 @@
|
||||
import { Stat, StatGroup, StatLabel, StatNumber } from '@chakra-ui/react';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useGetQueueStatusQuery } from 'services/api/endpoints/queue';
|
||||
import StatusStatGroup from './common/StatusStatGroup';
|
||||
import StatusStatItem from './common/StatusStatItem';
|
||||
|
||||
const QueueStatus = () => {
|
||||
const { data: queueStatus } = useGetQueueStatusQuery();
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<StatGroup alignItems="center" justifyContent="center" w="full" h="full">
|
||||
<Stat w={24}>
|
||||
<StatLabel>{t('queue.in_progress')}</StatLabel>
|
||||
<StatNumber>{queueStatus?.queue.in_progress ?? 0}</StatNumber>
|
||||
</Stat>
|
||||
<Stat w={24}>
|
||||
<StatLabel>{t('queue.pending')}</StatLabel>
|
||||
<StatNumber>{queueStatus?.queue.pending ?? 0}</StatNumber>
|
||||
</Stat>
|
||||
<Stat w={24}>
|
||||
<StatLabel>{t('queue.completed')}</StatLabel>
|
||||
<StatNumber>{queueStatus?.queue.completed ?? 0}</StatNumber>
|
||||
</Stat>
|
||||
<Stat w={24}>
|
||||
<StatLabel>{t('queue.failed')}</StatLabel>
|
||||
<StatNumber>{queueStatus?.queue.failed ?? 0}</StatNumber>
|
||||
</Stat>
|
||||
<Stat w={24}>
|
||||
<StatLabel>{t('queue.canceled')}</StatLabel>
|
||||
<StatNumber>{queueStatus?.queue.canceled ?? 0}</StatNumber>
|
||||
</Stat>
|
||||
<Stat w={24}>
|
||||
<StatLabel>{t('queue.total')}</StatLabel>
|
||||
<StatNumber>{queueStatus?.queue.total}</StatNumber>
|
||||
</Stat>
|
||||
</StatGroup>
|
||||
<StatusStatGroup>
|
||||
<StatusStatItem
|
||||
label={t('queue.in_progress')}
|
||||
value={queueStatus?.queue.in_progress ?? 0}
|
||||
/>
|
||||
<StatusStatItem
|
||||
label={t('queue.pending')}
|
||||
value={queueStatus?.queue.pending ?? 0}
|
||||
/>
|
||||
<StatusStatItem
|
||||
label={t('queue.completed')}
|
||||
value={queueStatus?.queue.completed ?? 0}
|
||||
/>
|
||||
<StatusStatItem
|
||||
label={t('queue.failed')}
|
||||
value={queueStatus?.queue.failed ?? 0}
|
||||
/>
|
||||
<StatusStatItem
|
||||
label={t('queue.canceled')}
|
||||
value={queueStatus?.queue.canceled ?? 0}
|
||||
/>
|
||||
<StatusStatItem
|
||||
label={t('queue.total')}
|
||||
value={queueStatus?.queue.total ?? 0}
|
||||
/>
|
||||
</StatusStatGroup>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -1,16 +1,14 @@
|
||||
import { Box, ButtonGroup, Flex } from '@chakra-ui/react';
|
||||
import { Box, Flex } from '@chakra-ui/react';
|
||||
import { memo } from 'react';
|
||||
import ClearQueueButton from './ClearQueueButton';
|
||||
import PauseProcessorButton from './PauseProcessorButton';
|
||||
import PruneQueueButton from './PruneQueueButton';
|
||||
import { useFeatureStatus } from '../../system/hooks/useFeatureStatus';
|
||||
import InvocationCacheStatus from './InvocationCacheStatus';
|
||||
import QueueList from './QueueList/QueueList';
|
||||
import QueueStatus from './QueueStatus';
|
||||
import ResumeProcessorButton from './ResumeProcessorButton';
|
||||
import { useFeatureStatus } from '../../system/hooks/useFeatureStatus';
|
||||
import QueueTabQueueControls from './QueueTabQueueControls';
|
||||
|
||||
const QueueTabContent = () => {
|
||||
const isPauseEnabled = useFeatureStatus('pauseQueue').isFeatureEnabled;
|
||||
const isResumeEnabled = useFeatureStatus('resumeQueue').isFeatureEnabled;
|
||||
const isInvocationCacheEnabled =
|
||||
useFeatureStatus('invocationCache').isFeatureEnabled;
|
||||
|
||||
return (
|
||||
<Flex
|
||||
@ -23,33 +21,9 @@ const QueueTabContent = () => {
|
||||
gap={2}
|
||||
>
|
||||
<Flex gap={2} w="full">
|
||||
<Flex layerStyle="second" borderRadius="base" p={2} gap={2}>
|
||||
{isPauseEnabled || isResumeEnabled ? (
|
||||
<ButtonGroup w={28} orientation="vertical" isAttached size="sm">
|
||||
{isResumeEnabled ? <ResumeProcessorButton /> : <></>}
|
||||
{isPauseEnabled ? <PauseProcessorButton /> : <></>}
|
||||
</ButtonGroup>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
<ButtonGroup w={28} orientation="vertical" isAttached size="sm">
|
||||
<PruneQueueButton />
|
||||
<ClearQueueButton />
|
||||
</ButtonGroup>
|
||||
</Flex>
|
||||
<Flex
|
||||
layerStyle="second"
|
||||
borderRadius="base"
|
||||
flexDir="column"
|
||||
py={2}
|
||||
px={3}
|
||||
gap={2}
|
||||
>
|
||||
<QueueTabQueueControls />
|
||||
<QueueStatus />
|
||||
</Flex>
|
||||
{/* <QueueStatusCard />
|
||||
<CurrentQueueItemCard />
|
||||
<NextQueueItemCard /> */}
|
||||
{isInvocationCacheEnabled && <InvocationCacheStatus />}
|
||||
</Flex>
|
||||
<Box layerStyle="second" p={2} borderRadius="base" w="full" h="full">
|
||||
<QueueList />
|
||||
|
@ -0,0 +1,30 @@
|
||||
import { ButtonGroup, Flex } from '@chakra-ui/react';
|
||||
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
||||
import { memo } from 'react';
|
||||
import ClearQueueButton from './ClearQueueButton';
|
||||
import PauseProcessorButton from './PauseProcessorButton';
|
||||
import PruneQueueButton from './PruneQueueButton';
|
||||
import ResumeProcessorButton from './ResumeProcessorButton';
|
||||
|
||||
const QueueTabQueueControls = () => {
|
||||
const isPauseEnabled = useFeatureStatus('pauseQueue').isFeatureEnabled;
|
||||
const isResumeEnabled = useFeatureStatus('resumeQueue').isFeatureEnabled;
|
||||
return (
|
||||
<Flex layerStyle="second" borderRadius="base" p={2} gap={2}>
|
||||
{isPauseEnabled || isResumeEnabled ? (
|
||||
<ButtonGroup w={28} orientation="vertical" isAttached size="sm">
|
||||
{isResumeEnabled ? <ResumeProcessorButton /> : <></>}
|
||||
{isPauseEnabled ? <PauseProcessorButton /> : <></>}
|
||||
</ButtonGroup>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
<ButtonGroup w={28} orientation="vertical" isAttached size="sm">
|
||||
<PruneQueueButton />
|
||||
<ClearQueueButton />
|
||||
</ButtonGroup>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(QueueTabQueueControls);
|
@ -0,0 +1,47 @@
|
||||
import IAIButton from 'common/components/IAIButton';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useGetInvocationCacheStatusQuery } from 'services/api/endpoints/appInfo';
|
||||
import { useDisableInvocationCache } from '../hooks/useDisableInvocationCache';
|
||||
import { useEnableInvocationCache } from '../hooks/useEnableInvocationCache';
|
||||
|
||||
const ToggleInvocationCacheButton = () => {
|
||||
const { t } = useTranslation();
|
||||
const { data: cacheStatus } = useGetInvocationCacheStatusQuery();
|
||||
|
||||
const {
|
||||
enableInvocationCache,
|
||||
isDisabled: isEnableDisabled,
|
||||
isLoading: isEnableLoading,
|
||||
} = useEnableInvocationCache();
|
||||
|
||||
const {
|
||||
disableInvocationCache,
|
||||
isDisabled: isDisableDisabled,
|
||||
isLoading: isDisableLoading,
|
||||
} = useDisableInvocationCache();
|
||||
|
||||
if (cacheStatus?.enabled) {
|
||||
return (
|
||||
<IAIButton
|
||||
isDisabled={isDisableDisabled}
|
||||
isLoading={isDisableLoading}
|
||||
onClick={disableInvocationCache}
|
||||
>
|
||||
{t('invocationCache.disable')}
|
||||
</IAIButton>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<IAIButton
|
||||
isDisabled={isEnableDisabled}
|
||||
isLoading={isEnableLoading}
|
||||
onClick={enableInvocationCache}
|
||||
>
|
||||
{t('invocationCache.enable')}
|
||||
</IAIButton>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(ToggleInvocationCacheButton);
|
@ -1,27 +0,0 @@
|
||||
import { ButtonGroup, ButtonGroupProps, Flex } from '@chakra-ui/react';
|
||||
import { memo } from 'react';
|
||||
import ClearQueueButton from './ClearQueueButton';
|
||||
import PauseProcessorButton from './PauseProcessorButton';
|
||||
import PruneQueueButton from './PruneQueueButton';
|
||||
import ResumeProcessorButton from './ResumeProcessorButton';
|
||||
|
||||
type Props = ButtonGroupProps & {
|
||||
asIconButtons?: boolean;
|
||||
};
|
||||
|
||||
const VerticalQueueControls = ({ asIconButtons, ...rest }: Props) => {
|
||||
return (
|
||||
<Flex flexDir="column" gap={2}>
|
||||
<ButtonGroup w="full" isAttached {...rest}>
|
||||
<ResumeProcessorButton asIconButton={asIconButtons} />
|
||||
<PauseProcessorButton asIconButton={asIconButtons} />
|
||||
</ButtonGroup>
|
||||
<ButtonGroup w="full" isAttached {...rest}>
|
||||
<PruneQueueButton asIconButton={asIconButtons} />
|
||||
<ClearQueueButton asIconButton={asIconButtons} />
|
||||
</ButtonGroup>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(VerticalQueueControls);
|
@ -0,0 +1,22 @@
|
||||
import { StatGroup, StatGroupProps } from '@chakra-ui/react';
|
||||
import { memo } from 'react';
|
||||
|
||||
const StatusStatGroup = ({ children, ...rest }: StatGroupProps) => (
|
||||
<StatGroup
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
w="full"
|
||||
h="full"
|
||||
layerStyle="second"
|
||||
borderRadius="base"
|
||||
py={2}
|
||||
px={3}
|
||||
gap={6}
|
||||
flexWrap="nowrap"
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
</StatGroup>
|
||||
);
|
||||
|
||||
export default memo(StatusStatGroup);
|
@ -0,0 +1,47 @@
|
||||
import {
|
||||
ChakraProps,
|
||||
Stat,
|
||||
StatLabel,
|
||||
StatNumber,
|
||||
StatProps,
|
||||
} from '@chakra-ui/react';
|
||||
import { memo } from 'react';
|
||||
|
||||
const sx: ChakraProps['sx'] = {
|
||||
'&[aria-disabled="true"]': {
|
||||
color: 'base.400',
|
||||
_dark: {
|
||||
color: 'base.500',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
type Props = Omit<StatProps, 'children'> & {
|
||||
label: string;
|
||||
value: string | number;
|
||||
isDisabled?: boolean;
|
||||
};
|
||||
|
||||
const StatusStatItem = ({
|
||||
label,
|
||||
value,
|
||||
isDisabled = false,
|
||||
...rest
|
||||
}: Props) => (
|
||||
<Stat
|
||||
flexGrow={1}
|
||||
textOverflow="ellipsis"
|
||||
overflow="hidden"
|
||||
whiteSpace="nowrap"
|
||||
aria-disabled={isDisabled}
|
||||
sx={sx}
|
||||
{...rest}
|
||||
>
|
||||
<StatLabel textOverflow="ellipsis" overflow="hidden" whiteSpace="nowrap">
|
||||
{label}
|
||||
</StatLabel>
|
||||
<StatNumber>{value}</StatNumber>
|
||||
</Stat>
|
||||
);
|
||||
|
||||
export default memo(StatusStatItem);
|
@ -0,0 +1,48 @@
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { addToast } from 'features/system/store/systemSlice';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
useClearInvocationCacheMutation,
|
||||
useGetInvocationCacheStatusQuery,
|
||||
} from 'services/api/endpoints/appInfo';
|
||||
|
||||
export const useClearInvocationCache = () => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const { data: cacheStatus } = useGetInvocationCacheStatusQuery();
|
||||
const isConnected = useAppSelector((state) => state.system.isConnected);
|
||||
const [trigger, { isLoading }] = useClearInvocationCacheMutation({
|
||||
fixedCacheKey: 'clearInvocationCache',
|
||||
});
|
||||
|
||||
const isDisabled = useMemo(
|
||||
() => !cacheStatus?.size || !isConnected,
|
||||
[cacheStatus?.size, isConnected]
|
||||
);
|
||||
|
||||
const clearInvocationCache = useCallback(async () => {
|
||||
if (isDisabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await trigger().unwrap();
|
||||
dispatch(
|
||||
addToast({
|
||||
title: t('invocationCache.clearSucceeded'),
|
||||
status: 'success',
|
||||
})
|
||||
);
|
||||
} catch {
|
||||
dispatch(
|
||||
addToast({
|
||||
title: t('invocationCache.clearFailed'),
|
||||
status: 'error',
|
||||
})
|
||||
);
|
||||
}
|
||||
}, [isDisabled, trigger, dispatch, t]);
|
||||
|
||||
return { clearInvocationCache, isLoading, cacheStatus, isDisabled };
|
||||
};
|
@ -0,0 +1,48 @@
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { addToast } from 'features/system/store/systemSlice';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
useDisableInvocationCacheMutation,
|
||||
useGetInvocationCacheStatusQuery,
|
||||
} from 'services/api/endpoints/appInfo';
|
||||
|
||||
export const useDisableInvocationCache = () => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const { data: cacheStatus } = useGetInvocationCacheStatusQuery();
|
||||
const isConnected = useAppSelector((state) => state.system.isConnected);
|
||||
const [trigger, { isLoading }] = useDisableInvocationCacheMutation({
|
||||
fixedCacheKey: 'disableInvocationCache',
|
||||
});
|
||||
|
||||
const isDisabled = useMemo(
|
||||
() => !cacheStatus?.enabled || !isConnected,
|
||||
[cacheStatus?.enabled, isConnected]
|
||||
);
|
||||
|
||||
const disableInvocationCache = useCallback(async () => {
|
||||
if (isDisabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await trigger().unwrap();
|
||||
dispatch(
|
||||
addToast({
|
||||
title: t('invocationCache.disableSucceeded'),
|
||||
status: 'success',
|
||||
})
|
||||
);
|
||||
} catch {
|
||||
dispatch(
|
||||
addToast({
|
||||
title: t('invocationCache.disableFailed'),
|
||||
status: 'error',
|
||||
})
|
||||
);
|
||||
}
|
||||
}, [isDisabled, trigger, dispatch, t]);
|
||||
|
||||
return { disableInvocationCache, isLoading, cacheStatus, isDisabled };
|
||||
};
|
@ -0,0 +1,48 @@
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { addToast } from 'features/system/store/systemSlice';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
useEnableInvocationCacheMutation,
|
||||
useGetInvocationCacheStatusQuery,
|
||||
} from 'services/api/endpoints/appInfo';
|
||||
|
||||
export const useEnableInvocationCache = () => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const { data: cacheStatus } = useGetInvocationCacheStatusQuery();
|
||||
const isConnected = useAppSelector((state) => state.system.isConnected);
|
||||
const [trigger, { isLoading }] = useEnableInvocationCacheMutation({
|
||||
fixedCacheKey: 'enableInvocationCache',
|
||||
});
|
||||
|
||||
const isDisabled = useMemo(
|
||||
() => cacheStatus?.enabled || !isConnected,
|
||||
[cacheStatus?.enabled, isConnected]
|
||||
);
|
||||
|
||||
const enableInvocationCache = useCallback(async () => {
|
||||
if (isDisabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await trigger().unwrap();
|
||||
dispatch(
|
||||
addToast({
|
||||
title: t('invocationCache.enableSucceeded'),
|
||||
status: 'success',
|
||||
})
|
||||
);
|
||||
} catch {
|
||||
dispatch(
|
||||
addToast({
|
||||
title: t('invocationCache.enableFailed'),
|
||||
status: 'error',
|
||||
})
|
||||
);
|
||||
}
|
||||
}, [isDisabled, trigger, dispatch, t]);
|
||||
|
||||
return { enableInvocationCache, isLoading, cacheStatus, isDisabled };
|
||||
};
|
@ -1,4 +1,5 @@
|
||||
import { api } from '..';
|
||||
import { paths } from '../schema';
|
||||
import { AppConfig, AppVersion } from '../types';
|
||||
|
||||
export const appInfoApi = api.injectEndpoints({
|
||||
@ -19,7 +20,45 @@ export const appInfoApi = api.injectEndpoints({
|
||||
providesTags: ['AppConfig'],
|
||||
keepUnusedDataFor: 86400000, // 1 day
|
||||
}),
|
||||
getInvocationCacheStatus: build.query<
|
||||
paths['/api/v1/app/invocation_cache/status']['get']['responses']['200']['content']['application/json'],
|
||||
void
|
||||
>({
|
||||
query: () => ({
|
||||
url: `app/invocation_cache/status`,
|
||||
method: 'GET',
|
||||
}),
|
||||
providesTags: ['InvocationCacheStatus'],
|
||||
}),
|
||||
clearInvocationCache: build.mutation<void, void>({
|
||||
query: () => ({
|
||||
url: `app/invocation_cache`,
|
||||
method: 'DELETE',
|
||||
}),
|
||||
invalidatesTags: ['InvocationCacheStatus'],
|
||||
}),
|
||||
enableInvocationCache: build.mutation<void, void>({
|
||||
query: () => ({
|
||||
url: `app/invocation_cache/enable`,
|
||||
method: 'PUT',
|
||||
}),
|
||||
invalidatesTags: ['InvocationCacheStatus'],
|
||||
}),
|
||||
disableInvocationCache: build.mutation<void, void>({
|
||||
query: () => ({
|
||||
url: `app/invocation_cache/disable`,
|
||||
method: 'PUT',
|
||||
}),
|
||||
invalidatesTags: ['InvocationCacheStatus'],
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
export const { useGetAppVersionQuery, useGetAppConfigQuery } = appInfoApi;
|
||||
export const {
|
||||
useGetAppVersionQuery,
|
||||
useGetAppConfigQuery,
|
||||
useClearInvocationCacheMutation,
|
||||
useDisableInvocationCacheMutation,
|
||||
useEnableInvocationCacheMutation,
|
||||
useGetInvocationCacheStatusQuery,
|
||||
} = appInfoApi;
|
||||
|
@ -24,6 +24,7 @@ export const tagTypes = [
|
||||
'SessionQueueStatus',
|
||||
'SessionProcessorStatus',
|
||||
'BatchStatus',
|
||||
'InvocationCacheStatus',
|
||||
];
|
||||
export type ApiTagDescription = TagDescription<(typeof tagTypes)[number]>;
|
||||
export const LIST_TAG = 'LIST';
|
||||
|
175
invokeai/frontend/web/src/services/api/schema.d.ts
vendored
175
invokeai/frontend/web/src/services/api/schema.d.ts
vendored
@ -317,6 +317,34 @@ export type paths = {
|
||||
*/
|
||||
post: operations["set_log_level"];
|
||||
};
|
||||
"/api/v1/app/invocation_cache": {
|
||||
/**
|
||||
* Clear Invocation Cache
|
||||
* @description Clears the invocation cache
|
||||
*/
|
||||
delete: operations["clear_invocation_cache"];
|
||||
};
|
||||
"/api/v1/app/invocation_cache/enable": {
|
||||
/**
|
||||
* Enable Invocation Cache
|
||||
* @description Clears the invocation cache
|
||||
*/
|
||||
put: operations["enable_invocation_cache"];
|
||||
};
|
||||
"/api/v1/app/invocation_cache/disable": {
|
||||
/**
|
||||
* Disable Invocation Cache
|
||||
* @description Clears the invocation cache
|
||||
*/
|
||||
put: operations["disable_invocation_cache"];
|
||||
};
|
||||
"/api/v1/app/invocation_cache/status": {
|
||||
/**
|
||||
* Get Invocation Cache Status
|
||||
* @description Clears the invocation cache
|
||||
*/
|
||||
get: operations["get_invocation_cache_status"];
|
||||
};
|
||||
"/api/v1/queue/{queue_id}/enqueue_graph": {
|
||||
/**
|
||||
* Enqueue Graph
|
||||
@ -1259,11 +1287,6 @@ export type components = {
|
||||
* @default true
|
||||
*/
|
||||
use_cache?: boolean;
|
||||
/**
|
||||
* CLIP
|
||||
* @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count
|
||||
*/
|
||||
clip?: components["schemas"]["ClipField"];
|
||||
/**
|
||||
* Skipped Layers
|
||||
* @description Number of layers to skip in text encoder
|
||||
@ -1276,6 +1299,11 @@ export type components = {
|
||||
* @enum {string}
|
||||
*/
|
||||
type: "clip_skip";
|
||||
/**
|
||||
* CLIP
|
||||
* @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count
|
||||
*/
|
||||
clip?: components["schemas"]["ClipField"];
|
||||
};
|
||||
/**
|
||||
* ClipSkipInvocationOutput
|
||||
@ -2020,7 +2048,7 @@ export type components = {
|
||||
* Clip Skip
|
||||
* @description The number of skipped CLIP layers
|
||||
*/
|
||||
clip_skip: number;
|
||||
clip_skip?: number;
|
||||
/**
|
||||
* Model
|
||||
* @description The main model used for inference
|
||||
@ -2309,10 +2337,7 @@ export type components = {
|
||||
* @enum {string}
|
||||
*/
|
||||
scheduler?: "ddim" | "ddpm" | "deis" | "lms" | "lms_k" | "pndm" | "heun" | "heun_k" | "euler" | "euler_k" | "euler_a" | "kdpm_2" | "kdpm_2_a" | "dpmpp_2s" | "dpmpp_2s_k" | "dpmpp_2m" | "dpmpp_2m_k" | "dpmpp_2m_sde" | "dpmpp_2m_sde_k" | "dpmpp_sde" | "dpmpp_sde_k" | "unipc";
|
||||
/**
|
||||
* Control
|
||||
* @description ControlNet(s) to apply
|
||||
*/
|
||||
/** Control */
|
||||
control?: components["schemas"]["ControlField"] | components["schemas"]["ControlField"][];
|
||||
/**
|
||||
* IP-Adapter
|
||||
@ -4718,6 +4743,34 @@ export type components = {
|
||||
*/
|
||||
type: "integer_output";
|
||||
};
|
||||
/** InvocationCacheStatus */
|
||||
InvocationCacheStatus: {
|
||||
/**
|
||||
* Size
|
||||
* @description The current size of the invocation cache
|
||||
*/
|
||||
size: number;
|
||||
/**
|
||||
* Hits
|
||||
* @description The number of cache hits
|
||||
*/
|
||||
hits: number;
|
||||
/**
|
||||
* Misses
|
||||
* @description The number of cache misses
|
||||
*/
|
||||
misses: number;
|
||||
/**
|
||||
* Enabled
|
||||
* @description Whether the invocation cache is enabled
|
||||
*/
|
||||
enabled: boolean;
|
||||
/**
|
||||
* Max Size
|
||||
* @description The maximum size of the invocation cache
|
||||
*/
|
||||
max_size: number;
|
||||
};
|
||||
/**
|
||||
* IterateInvocation
|
||||
* @description Iterates over a list of items
|
||||
@ -7497,11 +7550,6 @@ export type components = {
|
||||
* @default false
|
||||
*/
|
||||
use_cache?: boolean;
|
||||
/**
|
||||
* Image
|
||||
* @description The image to load
|
||||
*/
|
||||
image?: components["schemas"]["ImageField"];
|
||||
/**
|
||||
* Metadata
|
||||
* @description Optional core metadata to be written to image
|
||||
@ -7513,6 +7561,11 @@ export type components = {
|
||||
* @enum {string}
|
||||
*/
|
||||
type: "save_image";
|
||||
/**
|
||||
* Image
|
||||
* @description The image to load
|
||||
*/
|
||||
image?: components["schemas"]["ImageField"];
|
||||
};
|
||||
/**
|
||||
* Scale Latents
|
||||
@ -9042,18 +9095,6 @@ export type components = {
|
||||
/** Ui Order */
|
||||
ui_order?: number;
|
||||
};
|
||||
/**
|
||||
* IPAdapterModelFormat
|
||||
* @description An enumeration.
|
||||
* @enum {string}
|
||||
*/
|
||||
IPAdapterModelFormat: "invokeai";
|
||||
/**
|
||||
* ControlNetModelFormat
|
||||
* @description An enumeration.
|
||||
* @enum {string}
|
||||
*/
|
||||
ControlNetModelFormat: "checkpoint" | "diffusers";
|
||||
/**
|
||||
* StableDiffusionOnnxModelFormat
|
||||
* @description An enumeration.
|
||||
@ -9066,24 +9107,36 @@ export type components = {
|
||||
* @enum {string}
|
||||
*/
|
||||
StableDiffusion2ModelFormat: "checkpoint" | "diffusers";
|
||||
/**
|
||||
* StableDiffusion1ModelFormat
|
||||
* @description An enumeration.
|
||||
* @enum {string}
|
||||
*/
|
||||
StableDiffusion1ModelFormat: "checkpoint" | "diffusers";
|
||||
/**
|
||||
* CLIPVisionModelFormat
|
||||
* @description An enumeration.
|
||||
* @enum {string}
|
||||
*/
|
||||
CLIPVisionModelFormat: "diffusers";
|
||||
/**
|
||||
* StableDiffusion1ModelFormat
|
||||
* @description An enumeration.
|
||||
* @enum {string}
|
||||
*/
|
||||
StableDiffusion1ModelFormat: "checkpoint" | "diffusers";
|
||||
/**
|
||||
* StableDiffusionXLModelFormat
|
||||
* @description An enumeration.
|
||||
* @enum {string}
|
||||
*/
|
||||
StableDiffusionXLModelFormat: "checkpoint" | "diffusers";
|
||||
/**
|
||||
* IPAdapterModelFormat
|
||||
* @description An enumeration.
|
||||
* @enum {string}
|
||||
*/
|
||||
IPAdapterModelFormat: "invokeai";
|
||||
/**
|
||||
* ControlNetModelFormat
|
||||
* @description An enumeration.
|
||||
* @enum {string}
|
||||
*/
|
||||
ControlNetModelFormat: "checkpoint" | "diffusers";
|
||||
};
|
||||
responses: never;
|
||||
parameters: never;
|
||||
@ -10505,6 +10558,62 @@ export type operations = {
|
||||
};
|
||||
};
|
||||
};
|
||||
/**
|
||||
* Clear Invocation Cache
|
||||
* @description Clears the invocation cache
|
||||
*/
|
||||
clear_invocation_cache: {
|
||||
responses: {
|
||||
/** @description The operation was successful */
|
||||
200: {
|
||||
content: {
|
||||
"application/json": unknown;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
/**
|
||||
* Enable Invocation Cache
|
||||
* @description Clears the invocation cache
|
||||
*/
|
||||
enable_invocation_cache: {
|
||||
responses: {
|
||||
/** @description The operation was successful */
|
||||
200: {
|
||||
content: {
|
||||
"application/json": unknown;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
/**
|
||||
* Disable Invocation Cache
|
||||
* @description Clears the invocation cache
|
||||
*/
|
||||
disable_invocation_cache: {
|
||||
responses: {
|
||||
/** @description The operation was successful */
|
||||
200: {
|
||||
content: {
|
||||
"application/json": unknown;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
/**
|
||||
* Get Invocation Cache Status
|
||||
* @description Clears the invocation cache
|
||||
*/
|
||||
get_invocation_cache_status: {
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
200: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["InvocationCacheStatus"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
/**
|
||||
* Enqueue Graph
|
||||
* @description Enqueues a graph for single execution.
|
||||
|
Loading…
Reference in New Issue
Block a user