diff --git a/invokeai/app/api/routers/session_queue.py b/invokeai/app/api/routers/session_queue.py
index fe0c31bce9..d21610fed3 100644
--- a/invokeai/app/api/routers/session_queue.py
+++ b/invokeai/app/api/routers/session_queue.py
@@ -20,7 +20,6 @@ from invokeai.app.services.session_queue.session_queue_common import (
RetryItemsResult,
SessionQueueCountsByDestination,
SessionQueueItem,
- SessionQueueItemDTO,
SessionQueueStatus,
)
from invokeai.app.services.shared.pagination import CursorPaginatedResults
@@ -68,7 +67,7 @@ async def enqueue_batch(
"/{queue_id}/list",
operation_id="list_queue_items",
responses={
- 200: {"model": CursorPaginatedResults[SessionQueueItemDTO]},
+ 200: {"model": CursorPaginatedResults[SessionQueueItem]},
},
)
async def list_queue_items(
@@ -77,11 +76,38 @@ async def list_queue_items(
status: Optional[QUEUE_ITEM_STATUS] = Query(default=None, description="The status of items to fetch"),
cursor: Optional[int] = Query(default=None, description="The pagination cursor"),
priority: int = Query(default=0, description="The pagination cursor priority"),
-) -> CursorPaginatedResults[SessionQueueItemDTO]:
- """Gets all queue items (without graphs)"""
+ destination: Optional[str] = Query(default=None, description="The destination of queue items to fetch"),
+) -> CursorPaginatedResults[SessionQueueItem]:
+ """Gets cursor-paginated queue items"""
return ApiDependencies.invoker.services.session_queue.list_queue_items(
- queue_id=queue_id, limit=limit, status=status, cursor=cursor, priority=priority
+ queue_id=queue_id,
+ limit=limit,
+ status=status,
+ cursor=cursor,
+ priority=priority,
+ destination=destination,
+ )
+
+
+@session_queue_router.get(
+ "/{queue_id}/all",
+ operation_id="list_all_queue_items",
+ responses={
+ 200: {"model": list[SessionQueueItem]},
+ },
+)
+async def list_all_queue_items(
+ queue_id: str = Path(description="The queue id to perform this operation on"),
+ status: Optional[QUEUE_ITEM_STATUS] = Query(default=None, description="The status of items to fetch"),
+ destination: Optional[str] = Query(default=None, description="The destination of queue items to fetch"),
+) -> list[SessionQueueItem]:
+ """Gets all queue items"""
+
+ return ApiDependencies.invoker.services.session_queue.list_all_queue_items(
+ queue_id=queue_id,
+ status=status,
+ destination=destination,
)
diff --git a/invokeai/app/services/session_queue/session_queue_base.py b/invokeai/app/services/session_queue/session_queue_base.py
index 39dac82e23..187fa2e815 100644
--- a/invokeai/app/services/session_queue/session_queue_base.py
+++ b/invokeai/app/services/session_queue/session_queue_base.py
@@ -17,7 +17,6 @@ from invokeai.app.services.session_queue.session_queue_common import (
RetryItemsResult,
SessionQueueCountsByDestination,
SessionQueueItem,
- SessionQueueItemDTO,
SessionQueueStatus,
)
from invokeai.app.services.shared.graph import GraphExecutionState
@@ -127,10 +126,21 @@ class SessionQueueBase(ABC):
priority: int,
cursor: Optional[int] = None,
status: Optional[QUEUE_ITEM_STATUS] = None,
- ) -> CursorPaginatedResults[SessionQueueItemDTO]:
+ destination: Optional[str] = None,
+ ) -> CursorPaginatedResults[SessionQueueItem]:
"""Gets a page of session queue items"""
pass
+ @abstractmethod
+ def list_all_queue_items(
+ self,
+ queue_id: str,
+ status: Optional[QUEUE_ITEM_STATUS] = None,
+ destination: Optional[str] = None,
+ ) -> list[SessionQueueItem]:
+ """Gets all queue items that match the given parameters"""
+ pass
+
@abstractmethod
def get_queue_item(self, item_id: int) -> SessionQueueItem:
"""Gets a session queue item by ID"""
diff --git a/invokeai/app/services/session_queue/session_queue_common.py b/invokeai/app/services/session_queue/session_queue_common.py
index fafb07b49f..1861110c97 100644
--- a/invokeai/app/services/session_queue/session_queue_common.py
+++ b/invokeai/app/services/session_queue/session_queue_common.py
@@ -208,7 +208,7 @@ class FieldIdentifier(BaseModel):
user_label: str | None = Field(description="The user label of the field, if any")
-class SessionQueueItemWithoutGraph(BaseModel):
+class SessionQueueItem(BaseModel):
"""Session queue item without the full graph. Used for serialization."""
item_id: int = Field(description="The identifier of the session queue item")
@@ -252,42 +252,7 @@ class SessionQueueItemWithoutGraph(BaseModel):
default=None,
description="The ID of the published workflow associated with this queue item",
)
- api_input_fields: Optional[list[FieldIdentifier]] = Field(
- default=None, description="The fields that were used as input to the API"
- )
- api_output_fields: Optional[list[FieldIdentifier]] = Field(
- default=None, description="The nodes that were used as output from the API"
- )
credits: Optional[float] = Field(default=None, description="The total credits used for this queue item")
-
- @classmethod
- def queue_item_dto_from_dict(cls, queue_item_dict: dict) -> "SessionQueueItemDTO":
- # must parse these manually
- queue_item_dict["field_values"] = get_field_values(queue_item_dict)
- return SessionQueueItemDTO(**queue_item_dict)
-
- model_config = ConfigDict(
- json_schema_extra={
- "required": [
- "item_id",
- "status",
- "batch_id",
- "queue_id",
- "session_id",
- "priority",
- "session_id",
- "created_at",
- "updated_at",
- ]
- }
- )
-
-
-class SessionQueueItemDTO(SessionQueueItemWithoutGraph):
- pass
-
-
-class SessionQueueItem(SessionQueueItemWithoutGraph):
session: GraphExecutionState = Field(description="The fully-populated session to be executed")
workflow: Optional[WorkflowWithoutID] = Field(
default=None, description="The workflow associated with this queue item"
diff --git a/invokeai/app/services/session_queue/session_queue_sqlite.py b/invokeai/app/services/session_queue/session_queue_sqlite.py
index 3249441329..81239338ec 100644
--- a/invokeai/app/services/session_queue/session_queue_sqlite.py
+++ b/invokeai/app/services/session_queue/session_queue_sqlite.py
@@ -24,7 +24,6 @@ from invokeai.app.services.session_queue.session_queue_common import (
RetryItemsResult,
SessionQueueCountsByDestination,
SessionQueueItem,
- SessionQueueItemDTO,
SessionQueueItemNotFoundError,
SessionQueueStatus,
ValueToInsertTuple,
@@ -540,26 +539,12 @@ class SqliteSessionQueue(SessionQueueBase):
priority: int,
cursor: Optional[int] = None,
status: Optional[QUEUE_ITEM_STATUS] = None,
- ) -> CursorPaginatedResults[SessionQueueItemDTO]:
+ destination: Optional[str] = None,
+ ) -> CursorPaginatedResults[SessionQueueItem]:
cursor_ = self._conn.cursor()
item_id = cursor
query = """--sql
- SELECT item_id,
- status,
- priority,
- field_values,
- error_type,
- error_message,
- error_traceback,
- created_at,
- updated_at,
- completed_at,
- started_at,
- session_id,
- batch_id,
- queue_id,
- origin,
- destination
+ SELECT *
FROM session_queue
WHERE queue_id = ?
"""
@@ -571,6 +556,12 @@ class SqliteSessionQueue(SessionQueueBase):
"""
params.append(status)
+ if destination is not None:
+ query += """---sql
+ AND destination = ?
+ """
+ params.append(destination)
+
if item_id is not None:
query += """--sql
AND (priority < ?) OR (priority = ? AND item_id > ?)
@@ -586,7 +577,7 @@ class SqliteSessionQueue(SessionQueueBase):
params.append(limit + 1)
cursor_.execute(query, params)
results = cast(list[sqlite3.Row], cursor_.fetchall())
- items = [SessionQueueItemDTO.queue_item_dto_from_dict(dict(result)) for result in results]
+ items = [SessionQueueItem.queue_item_from_dict(dict(result)) for result in results]
has_more = False
if len(items) > limit:
# remove the extra item
@@ -594,6 +585,44 @@ class SqliteSessionQueue(SessionQueueBase):
has_more = True
return CursorPaginatedResults(items=items, limit=limit, has_more=has_more)
+ def list_all_queue_items(
+ self,
+ queue_id: str,
+ status: Optional[QUEUE_ITEM_STATUS] = None,
+ destination: Optional[str] = None,
+ ) -> list[SessionQueueItem]:
+ """Gets all queue items that match the given parameters"""
+ cursor_ = self._conn.cursor()
+ query = """--sql
+ SELECT *
+ FROM session_queue
+ WHERE queue_id = ?
+ """
+ params: list[Union[str, int]] = [queue_id]
+
+ if status is not None:
+ query += """--sql
+ AND status = ?
+ """
+ params.append(status)
+
+ if destination is not None:
+ query += """---sql
+ AND destination = ?
+ """
+ params.append(destination)
+
+ query += """--sql
+ ORDER BY
+ priority DESC,
+ item_id ASC
+ ;
+ """
+ cursor_.execute(query, params)
+ results = cast(list[sqlite3.Row], cursor_.fetchall())
+ items = [SessionQueueItem.queue_item_from_dict(dict(result)) for result in results]
+ return items
+
def get_queue_status(self, queue_id: str) -> SessionQueueStatus:
cursor = self._conn.cursor()
cursor.execute(
diff --git a/invokeai/app/services/shared/graph.py b/invokeai/app/services/shared/graph.py
index 9f22f2c6da..2706a0936a 100644
--- a/invokeai/app/services/shared/graph.py
+++ b/invokeai/app/services/shared/graph.py
@@ -7,6 +7,7 @@ from typing import Any, Optional, TypeVar, Union, get_args, get_origin, get_type
import networkx as nx
from pydantic import (
BaseModel,
+ ConfigDict,
GetCoreSchemaHandler,
GetJsonSchemaHandler,
ValidationError,
@@ -787,6 +788,22 @@ class GraphExecutionState(BaseModel):
default_factory=dict,
)
+ model_config = ConfigDict(
+ json_schema_extra={
+ "required": [
+ "id",
+ "graph",
+ "execution_graph",
+ "executed",
+ "executed_history",
+ "results",
+ "errors",
+ "prepared_source_mapping",
+ "source_prepared_mapping",
+ ]
+ }
+ )
+
@field_validator("graph")
def graph_is_valid(cls, v: Graph):
"""Validates that the graph is valid"""
diff --git a/invokeai/frontend/web/src/app/store/store.ts b/invokeai/frontend/web/src/app/store/store.ts
index 0a2f515334..a65416924d 100644
--- a/invokeai/frontend/web/src/app/store/store.ts
+++ b/invokeai/frontend/web/src/app/store/store.ts
@@ -3,7 +3,6 @@ import { autoBatchEnhancer, combineReducers, configureStore } from '@reduxjs/too
import { logger } from 'app/logging/logger';
import { idbKeyValDriver } from 'app/store/enhancers/reduxRemember/driver';
import { errorHandler } from 'app/store/enhancers/reduxRemember/errors';
-import { getDebugLoggerMiddleware } from 'app/store/middleware/debugLoggerMiddleware';
import { deepClone } from 'common/util/deepClone';
import { changeBoardModalSlice } from 'features/changeBoardModal/store/slice';
import { canvasSettingsPersistConfig, canvasSettingsSlice } from 'features/controlLayers/store/canvasSettingsSlice';
@@ -176,7 +175,7 @@ export const createStore = (uniqueStoreKey?: string, persist = true) =>
.concat(api.middleware)
.concat(dynamicMiddlewares)
.concat(authToastMiddleware)
- .concat(getDebugLoggerMiddleware())
+ // .concat(getDebugLoggerMiddleware())
.prepend(listenerMiddleware.middleware),
enhancers: (getDefaultEnhancers) => {
const _enhancers = getDefaultEnhancers().concat(autoBatchEnhancer());
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/CanvasMainPanelContent.tsx b/invokeai/frontend/web/src/features/controlLayers/components/CanvasMainPanelContent.tsx
index 7c61000b89..8f853f5d09 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/CanvasMainPanelContent.tsx
+++ b/invokeai/frontend/web/src/features/controlLayers/components/CanvasMainPanelContent.tsx
@@ -1,10 +1,14 @@
-import type { SystemStyleObject } from '@invoke-ai/ui-library';
+/* eslint-disable i18next/no-literal-string */
+import type { FlexProps, SystemStyleObject } from '@invoke-ai/ui-library';
import {
Box,
Button,
ButtonGroup,
+ CircularProgress,
ContextMenu,
Flex,
+ FormControl,
+ FormLabel,
Heading,
IconButton,
Image,
@@ -12,9 +16,13 @@ import {
MenuButton,
MenuList,
Spacer,
+ Switch,
Text,
+ Tooltip,
} from '@invoke-ai/ui-library';
import { useStore } from '@nanostores/react';
+import { skipToken } from '@reduxjs/toolkit/query';
+import { EMPTY_ARRAY } from 'app/store/constants';
import { useAppStore } from 'app/store/nanostores/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { FocusRegionWrapper } from 'common/components/FocusRegionWrapper';
@@ -49,15 +57,21 @@ import { newCanvasFromImageDndTarget } from 'features/dnd/dnd';
import { DndDropTarget } from 'features/dnd/DndDropTarget';
import { DndImage } from 'features/dnd/DndImage';
import { newCanvasFromImage } from 'features/imageActions/actions';
-import { memo, useCallback, useEffect, useMemo } from 'react';
+import type { ProgressImage } from 'features/nodes/types/common';
+import { isImageField } from 'features/nodes/types/common';
+import { isCanvasOutputNodeId } from 'features/nodes/util/graph/graphBuilderUtils';
+import type { ChangeEvent } from 'react';
+import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import { Trans, useTranslation } from 'react-i18next';
import { PiDotsThreeOutlineVerticalFill, PiUploadBold } from 'react-icons/pi';
-import type { ImageDTO } from 'services/api/types';
+import { getImageDTOSafe, useGetImageDTOQuery } from 'services/api/endpoints/images';
+import { queueItemsAdapterSelectors, useListQueueItemsQuery } from 'services/api/endpoints/queue';
+import type { ImageDTO, S } from 'services/api/types';
import type { ProgressAndResult } from 'services/events/stores';
-import { $progressImages, useMapSelector } from 'services/events/stores';
+import { $progressImages, $socket, useMapSelector } from 'services/events/stores';
import type { Equals, Param0 } from 'tsafe';
-import { assert } from 'tsafe';
+import { assert, objectEntries } from 'tsafe';
import { CanvasAlertsInvocationProgress } from './CanvasAlerts/CanvasAlertsInvocationProgress';
@@ -311,20 +325,405 @@ const SimpleActiveSession = memo(() => {
- Generations from this Session
+ Generations
-
-
-
+
);
});
SimpleActiveSession.displayName = 'SimpleActiveSession';
+const scrollIndicatorSx = {
+ opacity: 0,
+ '&[data-visible="true"]': {
+ opacity: 1,
+ },
+} satisfies SystemStyleObject;
+
+const StagingArea = memo(() => {
+ const [selectedItemId, setSelectedItemId] = useState(null);
+ const [autoSwitch, setAutoSwitch] = useState(true);
+ const [canScrollLeft, setCanScrollLeft] = useState(false);
+ const [canScrollRight, setCanScrollRight] = useState(false);
+ const scrollableRef = useRef(null);
+ const { data } = useListQueueItemsQuery({ destination: 'canvas' });
+ const items = useMemo(() => {
+ if (!data) {
+ return EMPTY_ARRAY;
+ }
+ return queueItemsAdapterSelectors.selectAll(data);
+ }, [data]);
+ const selectedItem = useMemo(
+ () =>
+ data && selectedItemId !== null ? queueItemsAdapterSelectors.selectById(data, String(selectedItemId)) : null,
+ [data, selectedItemId]
+ );
+
+ useEffect(() => {
+ if (items.length === 0) {
+ setSelectedItemId(null);
+ return;
+ }
+ if (selectedItem === null && items.length > 0) {
+ setSelectedItemId(items[0]?.item_id ?? null);
+ return;
+ }
+ if (selectedItemId === null || items.find((item) => item.item_id === selectedItemId) === undefined) {
+ return;
+ }
+ document.getElementById(`queue-item-status-card-${selectedItemId}`)?.scrollIntoView();
+ }, [items, selectedItem, selectedItemId]);
+
+ useEffect(() => {
+ const el = scrollableRef.current;
+ if (!el) {
+ return;
+ }
+ const onScroll = () => {
+ const { scrollLeft, scrollWidth, clientWidth } = el;
+ setCanScrollLeft(scrollLeft > 0);
+ setCanScrollRight(scrollLeft + clientWidth < scrollWidth);
+ };
+ el.addEventListener('scroll', onScroll);
+ const observer = new ResizeObserver(onScroll);
+ observer.observe(el);
+ return () => {
+ el.removeEventListener('scroll', onScroll);
+ observer.disconnect();
+ };
+ }, []);
+
+ const onSelectItem = useCallback((item: S['SessionQueueItem']) => {
+ setSelectedItemId(item.item_id);
+ if (item.status !== 'in_progress') {
+ setAutoSwitch(false);
+ }
+ }, []);
+
+ const onNext = useCallback(() => {
+ if (selectedItemId === null) {
+ return;
+ }
+ const currentIndex = items.findIndex((item) => item.item_id === selectedItemId);
+ const nextIndex = (currentIndex + 1) % items.length;
+ const nextItem = items[nextIndex];
+ if (!nextItem) {
+ return;
+ }
+ setSelectedItemId(nextItem.item_id);
+ }, [items, selectedItemId]);
+ const onPrev = useCallback(() => {
+ if (selectedItemId === null) {
+ return;
+ }
+ const currentIndex = items.findIndex((item) => item.item_id === selectedItemId);
+ const prevIndex = (currentIndex - 1 + items.length) % items.length;
+ const prevItem = items[prevIndex];
+ if (!prevItem) {
+ return;
+ }
+ setSelectedItemId(prevItem.item_id);
+ }, [items, selectedItemId]);
+
+ useHotkeys('left', onPrev);
+ useHotkeys('right', onNext);
+
+ const socket = useStore($socket);
+ useEffect(() => {
+ if (!autoSwitch) {
+ return;
+ }
+
+ if (!socket) {
+ return;
+ }
+
+ const onQueueItemStatusChanged = (data: S['QueueItemStatusChangedEvent']) => {
+ if (data.destination !== 'canvas') {
+ return;
+ }
+ if (data.status === 'in_progress') {
+ setSelectedItemId(data.item_id);
+ }
+ };
+
+ socket.on('queue_item_status_changed', onQueueItemStatusChanged);
+
+ return () => {
+ socket.off('queue_item_status_changed', onQueueItemStatusChanged);
+ };
+ }, [autoSwitch, socket]);
+
+ const onChangeAutoSwitch = useCallback((e: ChangeEvent) => {
+ setAutoSwitch(e.target.checked);
+ }, []);
+
+ return (
+
+
+ {selectedItem && }
+ {!selectedItem && No queued generations}
+
+
+ Auto-switch
+
+
+
+
+ {items.map((item, i) => (
+
+ ))}
+
+
+
+
+
+ );
+});
+StagingArea.displayName = 'StagingArea';
+
+const IMAGE_DTO_ERROR = Symbol('IMAGE_DTO_ERROR');
+
+const useOutputImageDTO = (item: S['SessionQueueItem']) => {
+ const [imageDTO, setImageDTO] = useState(null);
+ const syncImageDTO = useCallback(async (item: S['SessionQueueItem']) => {
+ const nodeId = Object.entries(item.session.source_prepared_mapping).find(([nodeId]) =>
+ isCanvasOutputNodeId(nodeId)
+ )?.[1][0];
+ const output = nodeId ? item.session.results[nodeId] : undefined;
+
+ if (!output) {
+ return setImageDTO(null);
+ }
+
+ for (const [_name, value] of objectEntries(output)) {
+ if (isImageField(value)) {
+ const imageDTO = await getImageDTOSafe(value.image_name);
+ if (imageDTO) {
+ setImageDTO(imageDTO);
+ $progressImages.setKey(item.session_id, undefined);
+ return;
+ }
+ }
+ }
+
+ setImageDTO(IMAGE_DTO_ERROR);
+ }, []);
+ useEffect(() => {
+ syncImageDTO(item);
+ }, [item, syncImageDTO]);
+
+ return imageDTO;
+};
+
+const QueueItemStatusCard = memo(
+ ({
+ item,
+ isSelected,
+ number,
+ onSelectItem,
+ ...rest
+ }: {
+ item: S['SessionQueueItem'];
+ isSelected: boolean;
+ number?: number;
+ onSelectItem?: (item: S['SessionQueueItem']) => void;
+ } & FlexProps) => {
+ const onClick = useCallback(() => {
+ onSelectItem?.(item);
+ }, [item, onSelectItem]);
+ return (
+
+
+ {number !== undefined && {`#${number}`}}
+
+ );
+ }
+);
+QueueItemStatusCard.displayName = 'QueueItemStatusCard';
+
+const QueueItemStatusCardContent = memo(({ item }: { item: S['SessionQueueItem'] }) => {
+ const socket = useStore($socket);
+ const [progressEvent, setProgressEvent] = useState(null);
+ const [progressImage, setProgressImage] = useState(null);
+ useEffect(() => {
+ if (!socket) {
+ return;
+ }
+ const onProgress = (data: S['InvocationProgressEvent']) => {
+ if (data.session_id !== item.session_id) {
+ return;
+ }
+ setProgressEvent(data);
+ if (data.image) {
+ setProgressImage(data.image);
+ }
+ };
+ socket.on('invocation_progress', onProgress);
+
+ return () => {
+ socket.off('invocation_progress', onProgress);
+ };
+ }, [item.session_id, socket]);
+
+ const imageDTO = useOutputImageDTO(item);
+
+ if (item.status === 'pending') {
+ return (
+
+ Pending
+
+ );
+ }
+ if (item.status === 'canceled') {
+ return (
+
+ Canceled
+
+ );
+ }
+ if (item.status === 'failed') {
+ return (
+
+ Failed
+
+ );
+ }
+ if (item.status === 'in_progress' || !imageDTO) {
+ if (!progressImage) {
+ return (
+ <>
+
+ In Progress
+
+
+ >
+ );
+ }
+ return (
+ <>
+
+
+ >
+ );
+ }
+ if (item.status === 'completed' && imageDTO && imageDTO !== IMAGE_DTO_ERROR) {
+ return ;
+ }
+
+ if (item.status === 'completed') {
+ return (
+
+ Unable to get image
+
+ );
+ }
+ assert>(false);
+});
+QueueItemStatusCardContent.displayName = 'QueueItemStatusCardContent';
+
+const circleStyles: SystemStyleObject = {
+ circle: {
+ transitionProperty: 'none',
+ transitionDuration: '0s',
+ },
+ position: 'absolute',
+ top: 2,
+ right: 2,
+};
+
+const ProgressCircle = ({ data }: { data?: S['InvocationProgressEvent'] | null }) => {
+ return (
+
+
+
+ );
+};
+ProgressCircle.displayName = 'ProgressCircle';
+
+const QueueItemResultCard = memo(({ item }: { item: S['SessionQueueItem'] }) => {
+ const imageName = useMemo(() => {
+ const nodeId = Object.entries(item.session.source_prepared_mapping).find(([nodeId]) =>
+ isCanvasOutputNodeId(nodeId)
+ )?.[1][0];
+ const output = nodeId ? item.session.results[nodeId] : undefined;
+ if (!output) {
+ return;
+ }
+
+ for (const [_name, value] of objectEntries(output)) {
+ if (isImageField(value)) {
+ return value.image_name;
+ }
+ }
+ }, [item]);
+
+ const { data: imageDTO } = useGetImageDTOQuery(imageName ?? skipToken);
+
+ if (!imageDTO) {
+ return Unknown output type;
+ }
+
+ return ;
+});
+QueueItemResultCard.displayName = 'QueueItemResultCard';
+
const SelectedImageOrProgressImage = memo(() => {
const selectedImage = useAppSelector(selectSelectedImage);
diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/graphBuilderUtils.ts b/invokeai/frontend/web/src/features/nodes/util/graph/graphBuilderUtils.ts
index dcce4be8b2..ce87203eeb 100644
--- a/invokeai/frontend/web/src/features/nodes/util/graph/graphBuilderUtils.ts
+++ b/invokeai/frontend/web/src/features/nodes/util/graph/graphBuilderUtils.ts
@@ -159,6 +159,8 @@ export const isMainModelWithoutUnet = (modelLoader: Invocation nodeId.split(':')[0] === CANVAS_OUTPUT_PREFIX;
+
export const isCanvasOutputEvent = (data: S['InvocationCompleteEvent']) => {
- return data.invocation_source_id.split(':')[0] === CANVAS_OUTPUT_PREFIX;
+ return isCanvasOutputNodeId(data.invocation_source_id);
};
diff --git a/invokeai/frontend/web/src/features/queue/components/QueueList/QueueItemComponent.tsx b/invokeai/frontend/web/src/features/queue/components/QueueList/QueueItemComponent.tsx
index 2b80dae71f..d645dcbfe5 100644
--- a/invokeai/frontend/web/src/features/queue/components/QueueList/QueueItemComponent.tsx
+++ b/invokeai/frontend/web/src/features/queue/components/QueueList/QueueItemComponent.tsx
@@ -13,7 +13,7 @@ import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { PiArrowCounterClockwiseBold, PiXBold } from 'react-icons/pi';
import { useSelector } from 'react-redux';
-import type { SessionQueueItemDTO } from 'services/api/types';
+import type { S } from 'services/api/types';
import { COLUMN_WIDTHS } from './constants';
import QueueItemDetail from './QueueItemDetail';
@@ -23,7 +23,7 @@ const selectedStyles = { bg: 'base.700' };
type InnerItemProps = {
index: number;
- item: SessionQueueItemDTO;
+ item: S['SessionQueueItem'];
context: ListContext;
};
@@ -155,7 +155,7 @@ const QueueItemComponent = ({ index, item, context }: InnerItemProps) => {
-
+
);
diff --git a/invokeai/frontend/web/src/features/queue/components/QueueList/QueueItemDetail.tsx b/invokeai/frontend/web/src/features/queue/components/QueueList/QueueItemDetail.tsx
index 1068f51a14..a06e82ac5b 100644
--- a/invokeai/frontend/web/src/features/queue/components/QueueList/QueueItemDetail.tsx
+++ b/invokeai/frontend/web/src/features/queue/components/QueueList/QueueItemDetail.tsx
@@ -12,24 +12,20 @@ import type { ReactNode } from 'react';
import { memo, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { PiArrowCounterClockwiseBold, PiXBold } from 'react-icons/pi';
-import { useGetQueueItemQuery } from 'services/api/endpoints/queue';
-import type { SessionQueueItemDTO } from 'services/api/types';
+import type { S } from 'services/api/types';
type Props = {
- queueItemDTO: SessionQueueItemDTO;
+ queueItem: S['SessionQueueItem'];
};
-const QueueItemComponent = ({ queueItemDTO }: Props) => {
- const { session_id, batch_id, item_id, origin, destination } = queueItemDTO;
+const QueueItemComponent = ({ queueItem }: Props) => {
+ const { session_id, batch_id, item_id, origin, destination } = queueItem;
const { t } = useTranslation();
const isRetryEnabled = useFeatureStatus('retryQueueItem');
const { cancelBatch, isLoading: isLoadingCancelBatch, isCanceled: isBatchCanceled } = useCancelBatch(batch_id);
-
const { cancelQueueItem, isLoading: isLoadingCancelQueueItem } = useCancelQueueItem(item_id);
const { retryQueueItem, isLoading: isLoadingRetryQueueItem } = useRetryQueueItem(item_id);
- const { data: queueItem } = useGetQueueItemQuery(item_id);
-
const originText = useOriginText(origin);
const destinationText = useDestinationText(destination);
diff --git a/invokeai/frontend/web/src/features/queue/components/QueueList/QueueList.tsx b/invokeai/frontend/web/src/features/queue/components/QueueList/QueueList.tsx
index c1af4436f2..06137aa409 100644
--- a/invokeai/frontend/web/src/features/queue/components/QueueList/QueueList.tsx
+++ b/invokeai/frontend/web/src/features/queue/components/QueueList/QueueList.tsx
@@ -14,7 +14,7 @@ import { useTranslation } from 'react-i18next';
import type { Components, ItemContent } from 'react-virtuoso';
import { Virtuoso } from 'react-virtuoso';
import { queueItemsAdapterSelectors, useListQueueItemsQuery } from 'services/api/endpoints/queue';
-import type { SessionQueueItemDTO } from 'services/api/types';
+import type { S } from 'services/api/types';
import QueueItemComponent from './QueueItemComponent';
import QueueListComponent from './QueueListComponent';
@@ -24,13 +24,13 @@ import type { ListContext } from './types';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type TableVirtuosoScrollerRef = (ref: HTMLElement | Window | null) => any;
-const computeItemKey = (index: number, item: SessionQueueItemDTO): number => item.item_id;
+const computeItemKey = (index: number, item: S['SessionQueueItem']): number => item.item_id;
-const components: Components = {
+const components: Components = {
List: QueueListComponent,
};
-const itemContent: ItemContent = (index, item, context) => (
+const itemContent: ItemContent = (index, item, context) => (
);
@@ -114,7 +114,7 @@ const QueueList = () => {
-
+
data={queueItems}
endReached={handleLoadMore}
scrollerRef={setScroller as TableVirtuosoScrollerRef}
diff --git a/invokeai/frontend/web/src/features/queue/components/QueueList/QueueListComponent.tsx b/invokeai/frontend/web/src/features/queue/components/QueueList/QueueListComponent.tsx
index f9ac99302e..932ef0d6d5 100644
--- a/invokeai/frontend/web/src/features/queue/components/QueueList/QueueListComponent.tsx
+++ b/invokeai/frontend/web/src/features/queue/components/QueueList/QueueListComponent.tsx
@@ -1,10 +1,10 @@
import { Flex, forwardRef, typedMemo } from '@invoke-ai/ui-library';
import type { Components } from 'react-virtuoso';
-import type { SessionQueueItemDTO } from 'services/api/types';
+import type { S } from 'services/api/types';
import type { ListContext } from './types';
-const QueueListComponent: Components['List'] = typedMemo(
+const QueueListComponent: Components['List'] = typedMemo(
forwardRef((props, ref) => {
return (
diff --git a/invokeai/frontend/web/src/features/queue/components/QueueList/useDestinationText.ts b/invokeai/frontend/web/src/features/queue/components/QueueList/useDestinationText.ts
index e72d1d709c..ab8ea27de8 100644
--- a/invokeai/frontend/web/src/features/queue/components/QueueList/useDestinationText.ts
+++ b/invokeai/frontend/web/src/features/queue/components/QueueList/useDestinationText.ts
@@ -1,7 +1,7 @@
import { useTranslation } from 'react-i18next';
-import type { SessionQueueItemDTO } from 'services/api/types';
+import type { S } from 'services/api/types';
-export const useDestinationText = (destination: SessionQueueItemDTO['destination']) => {
+export const useDestinationText = (destination: S['SessionQueueItem']['destination']) => {
const { t } = useTranslation();
if (destination === 'canvas') {
diff --git a/invokeai/frontend/web/src/features/queue/components/QueueList/useOriginText.ts b/invokeai/frontend/web/src/features/queue/components/QueueList/useOriginText.ts
index 9b3ac80272..deefbd08f4 100644
--- a/invokeai/frontend/web/src/features/queue/components/QueueList/useOriginText.ts
+++ b/invokeai/frontend/web/src/features/queue/components/QueueList/useOriginText.ts
@@ -1,7 +1,7 @@
import { useTranslation } from 'react-i18next';
-import type { SessionQueueItemDTO } from 'services/api/types';
+import type { S } from 'services/api/types';
-export const useOriginText = (origin: SessionQueueItemDTO['origin']) => {
+export const useOriginText = (origin: S['SessionQueueItem']['origin']) => {
const { t } = useTranslation();
if (origin === 'generation') {
diff --git a/invokeai/frontend/web/src/services/api/endpoints/queue.ts b/invokeai/frontend/web/src/services/api/endpoints/queue.ts
index 05906c2e56..fded3e8f09 100644
--- a/invokeai/frontend/web/src/services/api/endpoints/queue.ts
+++ b/invokeai/frontend/web/src/services/api/endpoints/queue.ts
@@ -5,9 +5,10 @@ import { $queueId } from 'app/store/nanostores/queueId';
import { listParamsReset } from 'features/queue/store/queueSlice';
import queryString from 'query-string';
import type { components, paths } from 'services/api/schema';
+import type { S } from 'services/api/types';
import type { ApiTagDescription } from '..';
-import { api, buildV1Url } from '..';
+import { api, buildV1Url, LIST_TAG } from '..';
/**
* Builds an endpoint URL for the queue router
@@ -35,7 +36,7 @@ export type SessionQueueItemStatus = NonNullable<
NonNullable['status']
>;
-export const queueItemsAdapter = createEntityAdapter({
+export const queueItemsAdapter = createEntityAdapter({
selectId: (queueItem) => String(queueItem.item_id),
sortComparer: (a, b) => {
// Sort by priority in descending order
@@ -388,10 +389,10 @@ export const queueApi = api.injectEndpoints({
invalidatesTags: ['CurrentSessionQueueItem', 'NextSessionQueueItem', 'QueueCountsByDestination'],
}),
listQueueItems: build.query<
- EntityState & {
+ EntityState & {
has_more: boolean;
},
- { cursor?: number; priority?: number } | undefined
+ { cursor?: number; priority?: number; destination?: string } | undefined
>({
query: (queryArgs) => ({
url: getListQueueItemsUrl(queryArgs),
@@ -400,20 +401,20 @@ export const queueApi = api.injectEndpoints({
serializeQueryArgs: () => {
return buildQueueUrl('list');
},
- transformResponse: (response: components['schemas']['CursorPaginatedResults_SessionQueueItemDTO_']) =>
- queueItemsAdapter.addMany(
+ transformResponse: (response: components['schemas']['CursorPaginatedResults_SessionQueueItem_']) =>
+ queueItemsAdapter.upsertMany(
queueItemsAdapter.getInitialState({
has_more: response.has_more,
}),
response.items
),
merge: (cache, response) => {
- queueItemsAdapter.addMany(cache, queueItemsAdapterSelectors.selectAll(response));
+ queueItemsAdapter.upsertMany(cache, queueItemsAdapterSelectors.selectAll(response));
cache.has_more = response.has_more;
},
forceRefetch: ({ currentArg, previousArg }) => currentArg !== previousArg,
keepUnusedDataFor: 60 * 5, // 5 minutes
- providesTags: ['FetchOnReconnect'],
+ providesTags: ['FetchOnReconnect', { type: 'SessionQueueItem', id: LIST_TAG }],
}),
getQueueCountsByDestination: build.query<
paths['/api/v1/queue/{queue_id}/counts_by_destination']['get']['responses']['200']['content']['application/json'],
diff --git a/invokeai/frontend/web/src/services/api/schema.ts b/invokeai/frontend/web/src/services/api/schema.ts
index 27c7c659e3..5d10535e66 100644
--- a/invokeai/frontend/web/src/services/api/schema.ts
+++ b/invokeai/frontend/web/src/services/api/schema.ts
@@ -1153,7 +1153,7 @@ export type paths = {
};
/**
* List Queue Items
- * @description Gets all queue items (without graphs)
+ * @description Gets cursor-paginated queue items
*/
get: operations["list_queue_items"];
put?: never;
@@ -1164,6 +1164,26 @@ export type paths = {
patch?: never;
trace?: never;
};
+ "/api/v1/queue/{queue_id}/all": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /**
+ * List All Queue Items
+ * @description Gets all queue items
+ */
+ get: operations["list_all_queue_items"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
"/api/v1/queue/{queue_id}/processor/resume": {
parameters: {
query?: never;
@@ -5715,8 +5735,8 @@ export type components = {
*/
type: "crop_latents";
};
- /** CursorPaginatedResults[SessionQueueItemDTO] */
- CursorPaginatedResults_SessionQueueItemDTO_: {
+ /** CursorPaginatedResults[SessionQueueItem] */
+ CursorPaginatedResults_SessionQueueItem_: {
/**
* Limit
* @description Limit of items to get
@@ -5731,7 +5751,7 @@ export type components = {
* Items
* @description Items
*/
- items: components["schemas"]["SessionQueueItemDTO"][];
+ items: components["schemas"]["SessionQueueItem"][];
};
/**
* OpenCV Inpaint
@@ -8742,47 +8762,47 @@ export type components = {
* Id
* @description The id of the execution state
*/
- id?: string;
+ id: string;
/** @description The graph being executed */
graph: components["schemas"]["Graph"];
/** @description The expanded graph of activated and executed nodes */
- execution_graph?: components["schemas"]["Graph"];
+ execution_graph: components["schemas"]["Graph"];
/**
* Executed
* @description The set of node ids that have been executed
*/
- executed?: string[];
+ executed: string[];
/**
* Executed History
* @description The list of node ids that have been executed, in order of execution
*/
- executed_history?: string[];
+ executed_history: string[];
/**
* Results
* @description The results of node executions
*/
- results?: {
+ results: {
[key: string]: components["schemas"]["BooleanCollectionOutput"] | components["schemas"]["BooleanOutput"] | components["schemas"]["BoundingBoxCollectionOutput"] | components["schemas"]["BoundingBoxOutput"] | components["schemas"]["CLIPOutput"] | components["schemas"]["CLIPSkipInvocationOutput"] | components["schemas"]["CalculateImageTilesOutput"] | components["schemas"]["CogView4ConditioningOutput"] | components["schemas"]["CogView4ModelLoaderOutput"] | components["schemas"]["CollectInvocationOutput"] | components["schemas"]["ColorCollectionOutput"] | components["schemas"]["ColorOutput"] | components["schemas"]["ConditioningCollectionOutput"] | components["schemas"]["ConditioningOutput"] | components["schemas"]["ControlOutput"] | components["schemas"]["DenoiseMaskOutput"] | components["schemas"]["FaceMaskOutput"] | components["schemas"]["FaceOffOutput"] | components["schemas"]["FloatCollectionOutput"] | components["schemas"]["FloatGeneratorOutput"] | components["schemas"]["FloatOutput"] | components["schemas"]["FluxConditioningOutput"] | components["schemas"]["FluxControlLoRALoaderOutput"] | components["schemas"]["FluxControlNetOutput"] | components["schemas"]["FluxFillOutput"] | components["schemas"]["FluxLoRALoaderOutput"] | components["schemas"]["FluxModelLoaderOutput"] | components["schemas"]["FluxReduxOutput"] | components["schemas"]["GradientMaskOutput"] | components["schemas"]["IPAdapterOutput"] | components["schemas"]["IdealSizeOutput"] | components["schemas"]["ImageCollectionOutput"] | components["schemas"]["ImageGeneratorOutput"] | components["schemas"]["ImageOutput"] | components["schemas"]["ImagePanelCoordinateOutput"] | components["schemas"]["IntegerCollectionOutput"] | components["schemas"]["IntegerGeneratorOutput"] | components["schemas"]["IntegerOutput"] | components["schemas"]["IterateInvocationOutput"] | components["schemas"]["LatentsCollectionOutput"] | components["schemas"]["LatentsMetaOutput"] | components["schemas"]["LatentsOutput"] | components["schemas"]["LoRALoaderOutput"] | components["schemas"]["LoRASelectorOutput"] | components["schemas"]["MDControlListOutput"] | components["schemas"]["MDIPAdapterListOutput"] | components["schemas"]["MDT2IAdapterListOutput"] | components["schemas"]["MaskOutput"] | components["schemas"]["MetadataItemOutput"] | components["schemas"]["MetadataOutput"] | components["schemas"]["MetadataToLorasCollectionOutput"] | components["schemas"]["MetadataToModelOutput"] | components["schemas"]["MetadataToSDXLModelOutput"] | components["schemas"]["ModelIdentifierOutput"] | components["schemas"]["ModelLoaderOutput"] | components["schemas"]["NoiseOutput"] | components["schemas"]["PairTileImageOutput"] | components["schemas"]["SD3ConditioningOutput"] | components["schemas"]["SDXLLoRALoaderOutput"] | components["schemas"]["SDXLModelLoaderOutput"] | components["schemas"]["SDXLRefinerModelLoaderOutput"] | components["schemas"]["SchedulerOutput"] | components["schemas"]["Sd3ModelLoaderOutput"] | components["schemas"]["SeamlessModeOutput"] | components["schemas"]["String2Output"] | components["schemas"]["StringCollectionOutput"] | components["schemas"]["StringGeneratorOutput"] | components["schemas"]["StringOutput"] | components["schemas"]["StringPosNegOutput"] | components["schemas"]["T2IAdapterOutput"] | components["schemas"]["TileToPropertiesOutput"] | components["schemas"]["UNetOutput"] | components["schemas"]["VAEOutput"];
};
/**
* Errors
* @description Errors raised when executing nodes
*/
- errors?: {
+ errors: {
[key: string]: string;
};
/**
* Prepared Source Mapping
* @description The map of prepared nodes to original graph nodes
*/
- prepared_source_mapping?: {
+ prepared_source_mapping: {
[key: string]: string;
};
/**
* Source Prepared Mapping
* @description The map of original graph nodes to prepared nodes
*/
- source_prepared_mapping?: {
+ source_prepared_mapping: {
[key: string]: string[];
};
};
@@ -19117,7 +19137,10 @@ export type components = {
*/
total: number;
};
- /** SessionQueueItem */
+ /**
+ * SessionQueueItem
+ * @description Session queue item without the full graph. Used for serialization.
+ */
SessionQueueItem: {
/**
* Item Id
@@ -19218,16 +19241,6 @@ export type components = {
* @description The ID of the published workflow associated with this queue item
*/
published_workflow_id?: string | null;
- /**
- * Api Input Fields
- * @description The fields that were used as input to the API
- */
- api_input_fields?: components["schemas"]["FieldIdentifier"][] | null;
- /**
- * Api Output Fields
- * @description The nodes that were used as output from the API
- */
- api_output_fields?: components["schemas"]["FieldIdentifier"][] | null;
/**
* Credits
* @description The total credits used for this queue item
@@ -19238,123 +19251,6 @@ export type components = {
/** @description The workflow associated with this queue item */
workflow?: components["schemas"]["WorkflowWithoutID"] | null;
};
- /** SessionQueueItemDTO */
- SessionQueueItemDTO: {
- /**
- * Item Id
- * @description The identifier of the session queue item
- */
- item_id: number;
- /**
- * Status
- * @description The status of this queue item
- * @default pending
- * @enum {string}
- */
- status: "pending" | "in_progress" | "completed" | "failed" | "canceled";
- /**
- * Priority
- * @description The priority of this queue item
- * @default 0
- */
- priority: number;
- /**
- * Batch Id
- * @description The ID of the batch associated with this queue item
- */
- batch_id: string;
- /**
- * Origin
- * @description The origin of this queue item. This data is used by the frontend to determine how to handle results.
- */
- origin?: string | null;
- /**
- * Destination
- * @description The origin of this queue item. This data is used by the frontend to determine how to handle results
- */
- destination?: string | null;
- /**
- * Session Id
- * @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.
- */
- session_id: string;
- /**
- * Error Type
- * @description The error type if this queue item errored
- */
- error_type?: string | null;
- /**
- * Error Message
- * @description The error message if this queue item errored
- */
- error_message?: string | null;
- /**
- * Error Traceback
- * @description The error traceback if this queue item errored
- */
- error_traceback?: string | null;
- /**
- * Created At
- * @description When this queue item was created
- */
- created_at: string;
- /**
- * Updated At
- * @description When this queue item was updated
- */
- updated_at: string;
- /**
- * Started At
- * @description When this queue item was started
- */
- started_at?: string | null;
- /**
- * Completed At
- * @description When this queue item was completed
- */
- completed_at?: string | null;
- /**
- * Queue Id
- * @description The id of the queue with which this item is associated
- */
- queue_id: string;
- /**
- * Field Values
- * @description The field values that were used for this queue item
- */
- field_values?: components["schemas"]["NodeFieldValue"][] | null;
- /**
- * Retried From Item Id
- * @description The item_id of the queue item that this item was retried from
- */
- retried_from_item_id?: number | null;
- /**
- * Is Api Validation Run
- * @description Whether this queue item is an API validation run.
- * @default false
- */
- is_api_validation_run?: boolean;
- /**
- * Published Workflow Id
- * @description The ID of the published workflow associated with this queue item
- */
- published_workflow_id?: string | null;
- /**
- * Api Input Fields
- * @description The fields that were used as input to the API
- */
- api_input_fields?: components["schemas"]["FieldIdentifier"][] | null;
- /**
- * Api Output Fields
- * @description The nodes that were used as output from the API
- */
- api_output_fields?: components["schemas"]["FieldIdentifier"][] | null;
- /**
- * Credits
- * @description The total credits used for this queue item
- */
- credits?: number | null;
- };
/** SessionQueueStatus */
SessionQueueStatus: {
/**
@@ -24476,6 +24372,8 @@ export interface operations {
cursor?: number | null;
/** @description The pagination cursor priority */
priority?: number;
+ /** @description The destination of queue items to fetch */
+ destination?: string | null;
};
header?: never;
path: {
@@ -24492,7 +24390,44 @@ export interface operations {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["CursorPaginatedResults_SessionQueueItemDTO_"];
+ "application/json": components["schemas"]["CursorPaginatedResults_SessionQueueItem_"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ list_all_queue_items: {
+ parameters: {
+ query?: {
+ /** @description The status of items to fetch */
+ status?: ("pending" | "in_progress" | "completed" | "failed" | "canceled") | null;
+ /** @description The destination of queue items to fetch */
+ destination?: string | null;
+ };
+ header?: never;
+ path: {
+ /** @description The queue id to perform this operation on */
+ queue_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["SessionQueueItem"][];
};
};
/** @description Validation Error */
diff --git a/invokeai/frontend/web/src/services/api/types.ts b/invokeai/frontend/web/src/services/api/types.ts
index 086ff3296f..5b7c234f1d 100644
--- a/invokeai/frontend/web/src/services/api/types.ts
+++ b/invokeai/frontend/web/src/services/api/types.ts
@@ -291,7 +291,6 @@ export type ModelInstallStatus = S['InstallStatus'];
export type Graph = S['Graph'];
export type NonNullableGraph = SetRequired;
export type Batch = S['Batch'];
-export type SessionQueueItemDTO = S['SessionQueueItemDTO'];
export type WorkflowRecordOrderBy = S['WorkflowRecordOrderBy'];
export type SQLiteDirection = S['SQLiteDirection'];
export type WorkflowRecordListItemWithThumbnailDTO = S['WorkflowRecordListItemWithThumbnailDTO'];
diff --git a/invokeai/frontend/web/src/services/events/setEventListeners.tsx b/invokeai/frontend/web/src/services/events/setEventListeners.tsx
index 1a873f5d43..357b86c7de 100644
--- a/invokeai/frontend/web/src/services/events/setEventListeners.tsx
+++ b/invokeai/frontend/web/src/services/events/setEventListeners.tsx
@@ -8,9 +8,7 @@ import { $bulkDownloadId } from 'app/store/nanostores/bulkDownloadId';
import { $queueId } from 'app/store/nanostores/queueId';
import type { AppStore } from 'app/store/store';
import { deepClone } from 'common/util/deepClone';
-import {
- stagingAreaGenerationStarted,
-} from 'features/controlLayers/store/canvasStagingAreaSlice';
+import { stagingAreaGenerationStarted } from 'features/controlLayers/store/canvasStagingAreaSlice';
import {
$isInPublishFlow,
$outputNodeId,
@@ -25,7 +23,7 @@ import { forEach, isNil, round } from 'lodash-es';
import type { ApiTagDescription } from 'services/api';
import { api, LIST_TAG } from 'services/api';
import { modelsApi } from 'services/api/endpoints/models';
-import { queueApi, queueItemsAdapter } from 'services/api/endpoints/queue';
+import { queueApi } from 'services/api/endpoints/queue';
import { workflowsApi } from 'services/api/endpoints/workflows';
import { buildOnInvocationComplete } from 'services/events/onInvocationComplete';
import { buildOnModelInstallError } from 'services/events/onModelInstallError';
@@ -383,24 +381,24 @@ export const setEventListeners = ({ socket, store, setIsConnected }: SetEventLis
log.debug({ data }, `Queue item ${item_id} status updated: ${status}`);
- // Update this specific queue item in the list of queue items (this is the queue item DTO, without the session)
- dispatch(
- queueApi.util.updateQueryData('listQueueItems', undefined, (draft) => {
- queueItemsAdapter.updateOne(draft, {
- id: String(item_id),
- changes: {
- status,
- started_at,
- updated_at: updated_at ?? undefined,
- completed_at: completed_at ?? undefined,
- error_type,
- error_message,
- error_traceback,
- credits,
- },
- });
- })
- );
+ // // Update this specific queue item in the list of queue items (this is the queue item DTO, without the session)
+ // dispatch(
+ // queueApi.util.updateQueryData('listQueueItems', undefined, (draft) => {
+ // queueItemsAdapter.updateOne(draft, {
+ // id: String(item_id),
+ // changes: {
+ // status,
+ // started_at,
+ // updated_at: updated_at ?? undefined,
+ // completed_at: completed_at ?? undefined,
+ // error_type,
+ // error_message,
+ // error_traceback,
+ // credits,
+ // },
+ // });
+ // })
+ // );
// Optimistic update of the queue status. We prefer to do an optimistic update over tag invalidation due to the
// frequency of `queue_item_status_changed` events.
@@ -426,6 +424,7 @@ export const setEventListeners = ({ socket, store, setIsConnected }: SetEventLis
'NextSessionQueueItem',
'InvocationCacheStatus',
{ type: 'SessionQueueItem', id: item_id },
+ { type: 'SessionQueueItem', id: LIST_TAG },
];
if (destination) {
tagsToInvalidate.push({ type: 'QueueCountsByDestination', id: destination });