mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-01-23 13:28:00 -05:00
Compare commits
2 Commits
psyche/boa
...
v4.2.6post
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7905a46ca4 | ||
|
|
38343917f8 |
@@ -5,7 +5,7 @@ from fastapi.routing import APIRouter
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from invokeai.app.api.dependencies import ApiDependencies
|
||||
from invokeai.app.services.board_records.board_records_common import BoardChanges, UncategorizedImageCounts
|
||||
from invokeai.app.services.board_records.board_records_common import BoardChanges
|
||||
from invokeai.app.services.boards.boards_common import BoardDTO
|
||||
from invokeai.app.services.shared.pagination import OffsetPaginatedResults
|
||||
|
||||
@@ -146,14 +146,3 @@ async def list_all_board_image_names(
|
||||
board_id,
|
||||
)
|
||||
return image_names
|
||||
|
||||
|
||||
@boards_router.get(
|
||||
"/uncategorized/counts",
|
||||
operation_id="get_uncategorized_image_counts",
|
||||
response_model=UncategorizedImageCounts,
|
||||
)
|
||||
async def get_uncategorized_image_counts() -> UncategorizedImageCounts:
|
||||
"""Gets count of images and assets for uncategorized images (images with no board assocation)"""
|
||||
|
||||
return ApiDependencies.invoker.services.board_records.get_uncategorized_image_counts()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
from invokeai.app.services.board_records.board_records_common import BoardChanges, BoardRecord, UncategorizedImageCounts
|
||||
from invokeai.app.services.board_records.board_records_common import BoardChanges, BoardRecord
|
||||
from invokeai.app.services.shared.pagination import OffsetPaginatedResults
|
||||
|
||||
|
||||
@@ -48,8 +48,3 @@ class BoardRecordStorageBase(ABC):
|
||||
def get_all(self, include_archived: bool = False) -> list[BoardRecord]:
|
||||
"""Gets all board records."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_uncategorized_image_counts(self) -> UncategorizedImageCounts:
|
||||
"""Gets count of images and assets for uncategorized images (images with no board assocation)."""
|
||||
pass
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from datetime import datetime
|
||||
from typing import Any, Optional, Union
|
||||
from typing import Optional, Union
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
@@ -26,25 +26,21 @@ class BoardRecord(BaseModelExcludeNull):
|
||||
"""Whether or not the board is archived."""
|
||||
is_private: Optional[bool] = Field(default=None, description="Whether the board is private.")
|
||||
"""Whether the board is private."""
|
||||
image_count: int = Field(description="The number of images in the board.")
|
||||
asset_count: int = Field(description="The number of assets in the board.")
|
||||
|
||||
|
||||
def deserialize_board_record(board_dict: dict[str, Any]) -> BoardRecord:
|
||||
def deserialize_board_record(board_dict: dict) -> BoardRecord:
|
||||
"""Deserializes a board record."""
|
||||
|
||||
# Retrieve all the values, setting "reasonable" defaults if they are not present.
|
||||
|
||||
board_id = board_dict.get("board_id", "unknown")
|
||||
board_name = board_dict.get("board_name", "unknown")
|
||||
cover_image_name = board_dict.get("cover_image_name", None)
|
||||
cover_image_name = board_dict.get("cover_image_name", "unknown")
|
||||
created_at = board_dict.get("created_at", get_iso_timestamp())
|
||||
updated_at = board_dict.get("updated_at", get_iso_timestamp())
|
||||
deleted_at = board_dict.get("deleted_at", get_iso_timestamp())
|
||||
archived = board_dict.get("archived", False)
|
||||
is_private = board_dict.get("is_private", False)
|
||||
image_count = board_dict.get("image_count", 0)
|
||||
asset_count = board_dict.get("asset_count", 0)
|
||||
|
||||
return BoardRecord(
|
||||
board_id=board_id,
|
||||
@@ -55,8 +51,6 @@ def deserialize_board_record(board_dict: dict[str, Any]) -> BoardRecord:
|
||||
deleted_at=deleted_at,
|
||||
archived=archived,
|
||||
is_private=is_private,
|
||||
image_count=image_count,
|
||||
asset_count=asset_count,
|
||||
)
|
||||
|
||||
|
||||
@@ -69,24 +63,19 @@ class BoardChanges(BaseModel, extra="forbid"):
|
||||
class BoardRecordNotFoundException(Exception):
|
||||
"""Raised when an board record is not found."""
|
||||
|
||||
def __init__(self, message: str = "Board record not found"):
|
||||
def __init__(self, message="Board record not found"):
|
||||
super().__init__(message)
|
||||
|
||||
|
||||
class BoardRecordSaveException(Exception):
|
||||
"""Raised when an board record cannot be saved."""
|
||||
|
||||
def __init__(self, message: str = "Board record not saved"):
|
||||
def __init__(self, message="Board record not saved"):
|
||||
super().__init__(message)
|
||||
|
||||
|
||||
class BoardRecordDeleteException(Exception):
|
||||
"""Raised when an board record cannot be deleted."""
|
||||
|
||||
def __init__(self, message: str = "Board record not deleted"):
|
||||
def __init__(self, message="Board record not deleted"):
|
||||
super().__init__(message)
|
||||
|
||||
|
||||
class UncategorizedImageCounts(BaseModel):
|
||||
image_count: int = Field(description="The number of uncategorized images.")
|
||||
asset_count: int = Field(description="The number of uncategorized assets.")
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import sqlite3
|
||||
import threading
|
||||
from dataclasses import dataclass
|
||||
from typing import Union, cast
|
||||
|
||||
from invokeai.app.services.board_records.board_records_base import BoardRecordStorageBase
|
||||
@@ -10,108 +9,12 @@ from invokeai.app.services.board_records.board_records_common import (
|
||||
BoardRecordDeleteException,
|
||||
BoardRecordNotFoundException,
|
||||
BoardRecordSaveException,
|
||||
UncategorizedImageCounts,
|
||||
deserialize_board_record,
|
||||
)
|
||||
from invokeai.app.services.shared.pagination import OffsetPaginatedResults
|
||||
from invokeai.app.services.shared.sqlite.sqlite_database import SqliteDatabase
|
||||
from invokeai.app.util.misc import uuid_string
|
||||
|
||||
BASE_BOARD_RECORD_QUERY = """
|
||||
-- This query retrieves board records, joining with the board_images and images tables to get image counts and cover image names.
|
||||
-- It is not a complete query, as it is missing a GROUP BY or WHERE clause (and is unterminated).
|
||||
SELECT b.board_id,
|
||||
b.board_name,
|
||||
b.created_at,
|
||||
b.updated_at,
|
||||
b.archived,
|
||||
-- Count the number of images in the board, alias image_count
|
||||
COUNT(
|
||||
CASE
|
||||
WHEN i.image_category in ('general') -- "Images" are images in the 'general' category
|
||||
AND i.is_intermediate = 0 THEN 1 -- Intermediates are not counted
|
||||
END
|
||||
) AS image_count,
|
||||
-- Count the number of assets in the board, alias asset_count
|
||||
COUNT(
|
||||
CASE
|
||||
WHEN i.image_category in ('control', 'mask', 'user', 'other') -- "Assets" are images in any of the other categories ('control', 'mask', 'user', 'other')
|
||||
AND i.is_intermediate = 0 THEN 1 -- Intermediates are not counted
|
||||
END
|
||||
) AS asset_count,
|
||||
-- Get the name of the the most recent image in the board, alias cover_image_name
|
||||
(
|
||||
SELECT bi.image_name
|
||||
FROM board_images bi
|
||||
JOIN images i ON bi.image_name = i.image_name
|
||||
WHERE bi.board_id = b.board_id
|
||||
AND i.is_intermediate = 0 -- Intermediates cannot be cover images
|
||||
ORDER BY i.created_at DESC -- Sort by created_at to get the most recent image
|
||||
LIMIT 1
|
||||
) AS cover_image_name
|
||||
FROM boards b
|
||||
LEFT JOIN board_images bi ON b.board_id = bi.board_id
|
||||
LEFT JOIN images i ON bi.image_name = i.image_name
|
||||
"""
|
||||
|
||||
|
||||
@dataclass
|
||||
class PaginatedBoardRecordsQueries:
|
||||
main_query: str
|
||||
total_count_query: str
|
||||
|
||||
|
||||
def get_paginated_list_board_records_queries(include_archived: bool) -> PaginatedBoardRecordsQueries:
|
||||
"""Gets a query to retrieve a paginated list of board records."""
|
||||
|
||||
archived_condition = "WHERE b.archived = 0" if not include_archived else ""
|
||||
|
||||
# The GROUP BY must be added _after_ the WHERE clause!
|
||||
main_query = f"""
|
||||
{BASE_BOARD_RECORD_QUERY}
|
||||
{archived_condition}
|
||||
GROUP BY b.board_id,
|
||||
b.board_name,
|
||||
b.created_at,
|
||||
b.updated_at
|
||||
ORDER BY b.created_at DESC
|
||||
LIMIT ? OFFSET ?;
|
||||
"""
|
||||
|
||||
total_count_query = f"""
|
||||
SELECT COUNT(*)
|
||||
FROM boards b
|
||||
{archived_condition};
|
||||
"""
|
||||
|
||||
return PaginatedBoardRecordsQueries(main_query=main_query, total_count_query=total_count_query)
|
||||
|
||||
|
||||
def get_list_all_board_records_query(include_archived: bool) -> str:
|
||||
"""Gets a query to retrieve all board records."""
|
||||
|
||||
archived_condition = "WHERE b.archived = 0" if not include_archived else ""
|
||||
|
||||
# The GROUP BY must be added _after_ the WHERE clause!
|
||||
return f"""
|
||||
{BASE_BOARD_RECORD_QUERY}
|
||||
{archived_condition}
|
||||
GROUP BY b.board_id,
|
||||
b.board_name,
|
||||
b.created_at,
|
||||
b.updated_at
|
||||
ORDER BY b.created_at DESC;
|
||||
"""
|
||||
|
||||
|
||||
def get_board_record_query() -> str:
|
||||
"""Gets a query to retrieve a board record."""
|
||||
|
||||
return f"""
|
||||
{BASE_BOARD_RECORD_QUERY}
|
||||
WHERE b.board_id = ?;
|
||||
"""
|
||||
|
||||
|
||||
class SqliteBoardRecordStorage(BoardRecordStorageBase):
|
||||
_conn: sqlite3.Connection
|
||||
@@ -173,7 +76,11 @@ class SqliteBoardRecordStorage(BoardRecordStorageBase):
|
||||
try:
|
||||
self._lock.acquire()
|
||||
self._cursor.execute(
|
||||
get_board_record_query(),
|
||||
"""--sql
|
||||
SELECT *
|
||||
FROM boards
|
||||
WHERE board_id = ?;
|
||||
""",
|
||||
(board_id,),
|
||||
)
|
||||
|
||||
@@ -185,7 +92,7 @@ class SqliteBoardRecordStorage(BoardRecordStorageBase):
|
||||
self._lock.release()
|
||||
if result is None:
|
||||
raise BoardRecordNotFoundException
|
||||
return deserialize_board_record(dict(result))
|
||||
return BoardRecord(**dict(result))
|
||||
|
||||
def update(
|
||||
self,
|
||||
@@ -242,17 +149,45 @@ class SqliteBoardRecordStorage(BoardRecordStorageBase):
|
||||
try:
|
||||
self._lock.acquire()
|
||||
|
||||
queries = get_paginated_list_board_records_queries(include_archived=include_archived)
|
||||
# Build base query
|
||||
base_query = """
|
||||
SELECT *
|
||||
FROM boards
|
||||
{archived_filter}
|
||||
ORDER BY created_at DESC
|
||||
LIMIT ? OFFSET ?;
|
||||
"""
|
||||
|
||||
self._cursor.execute(
|
||||
queries.main_query,
|
||||
(limit, offset),
|
||||
)
|
||||
# Determine archived filter condition
|
||||
if include_archived:
|
||||
archived_filter = ""
|
||||
else:
|
||||
archived_filter = "WHERE archived = 0"
|
||||
|
||||
final_query = base_query.format(archived_filter=archived_filter)
|
||||
|
||||
# Execute query to fetch boards
|
||||
self._cursor.execute(final_query, (limit, offset))
|
||||
|
||||
result = cast(list[sqlite3.Row], self._cursor.fetchall())
|
||||
boards = [deserialize_board_record(dict(r)) for r in result]
|
||||
|
||||
self._cursor.execute(queries.total_count_query)
|
||||
# Determine count query
|
||||
if include_archived:
|
||||
count_query = """
|
||||
SELECT COUNT(*)
|
||||
FROM boards;
|
||||
"""
|
||||
else:
|
||||
count_query = """
|
||||
SELECT COUNT(*)
|
||||
FROM boards
|
||||
WHERE archived = 0;
|
||||
"""
|
||||
|
||||
# Execute count query
|
||||
self._cursor.execute(count_query)
|
||||
|
||||
count = cast(int, self._cursor.fetchone()[0])
|
||||
|
||||
return OffsetPaginatedResults[BoardRecord](items=boards, offset=offset, limit=limit, total=count)
|
||||
@@ -266,9 +201,26 @@ class SqliteBoardRecordStorage(BoardRecordStorageBase):
|
||||
def get_all(self, include_archived: bool = False) -> list[BoardRecord]:
|
||||
try:
|
||||
self._lock.acquire()
|
||||
self._cursor.execute(get_list_all_board_records_query(include_archived=include_archived))
|
||||
|
||||
base_query = """
|
||||
SELECT *
|
||||
FROM boards
|
||||
{archived_filter}
|
||||
ORDER BY created_at DESC
|
||||
"""
|
||||
|
||||
if include_archived:
|
||||
archived_filter = ""
|
||||
else:
|
||||
archived_filter = "WHERE archived = 0"
|
||||
|
||||
final_query = base_query.format(archived_filter=archived_filter)
|
||||
|
||||
self._cursor.execute(final_query)
|
||||
|
||||
result = cast(list[sqlite3.Row], self._cursor.fetchall())
|
||||
boards = [deserialize_board_record(dict(r)) for r in result]
|
||||
|
||||
return boards
|
||||
|
||||
except sqlite3.Error as e:
|
||||
@@ -276,28 +228,3 @@ class SqliteBoardRecordStorage(BoardRecordStorageBase):
|
||||
raise e
|
||||
finally:
|
||||
self._lock.release()
|
||||
|
||||
def get_uncategorized_image_counts(self) -> UncategorizedImageCounts:
|
||||
try:
|
||||
self._lock.acquire()
|
||||
query = """
|
||||
-- Get the count of uncategorized images and assets.
|
||||
SELECT
|
||||
CASE
|
||||
WHEN i.image_category = 'general' THEN 'image_count' -- "Images" are images in the 'general' category
|
||||
ELSE 'asset_count' -- "Assets" are images in any of the other categories ('control', 'mask', 'user', 'other')
|
||||
END AS category_type,
|
||||
COUNT(*) AS unassigned_count
|
||||
FROM images i
|
||||
LEFT JOIN board_images bi ON i.image_name = bi.image_name
|
||||
WHERE bi.board_id IS NULL -- Uncategorized images have no board association
|
||||
AND i.is_intermediate = 0 -- Omit intermediates from the counts
|
||||
GROUP BY category_type; -- Group by category_type alias, as derived from the image_category column earlier
|
||||
"""
|
||||
self._cursor.execute(query)
|
||||
results = self._cursor.fetchall()
|
||||
image_count = dict(results)["image_count"]
|
||||
asset_count = dict(results)["asset_count"]
|
||||
return UncategorizedImageCounts(image_count=image_count, asset_count=asset_count)
|
||||
finally:
|
||||
self._lock.release()
|
||||
|
||||
@@ -1,8 +1,23 @@
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from invokeai.app.services.board_records.board_records_common import BoardRecord
|
||||
|
||||
|
||||
# TODO(psyche): BoardDTO is now identical to BoardRecord. We should consider removing it.
|
||||
class BoardDTO(BoardRecord):
|
||||
"""Deserialized board record."""
|
||||
"""Deserialized board record with cover image URL and image count."""
|
||||
|
||||
pass
|
||||
cover_image_name: Optional[str] = Field(description="The name of the board's cover image.")
|
||||
"""The URL of the thumbnail of the most recent image in the board."""
|
||||
image_count: int = Field(description="The number of images in the board.")
|
||||
"""The number of images in the board."""
|
||||
|
||||
|
||||
def board_record_to_dto(board_record: BoardRecord, cover_image_name: Optional[str], image_count: int) -> BoardDTO:
|
||||
"""Converts a board record to a board DTO."""
|
||||
return BoardDTO(
|
||||
**board_record.model_dump(exclude={"cover_image_name"}),
|
||||
cover_image_name=cover_image_name,
|
||||
image_count=image_count,
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from invokeai.app.services.board_records.board_records_common import BoardChanges
|
||||
from invokeai.app.services.boards.boards_base import BoardServiceABC
|
||||
from invokeai.app.services.boards.boards_common import BoardDTO
|
||||
from invokeai.app.services.boards.boards_common import BoardDTO, board_record_to_dto
|
||||
from invokeai.app.services.invoker import Invoker
|
||||
from invokeai.app.services.shared.pagination import OffsetPaginatedResults
|
||||
|
||||
@@ -16,11 +16,17 @@ class BoardService(BoardServiceABC):
|
||||
board_name: str,
|
||||
) -> BoardDTO:
|
||||
board_record = self.__invoker.services.board_records.save(board_name)
|
||||
return BoardDTO.model_validate(board_record.model_dump())
|
||||
return board_record_to_dto(board_record, None, 0)
|
||||
|
||||
def get_dto(self, board_id: str) -> BoardDTO:
|
||||
board_record = self.__invoker.services.board_records.get(board_id)
|
||||
return BoardDTO.model_validate(board_record.model_dump())
|
||||
cover_image = self.__invoker.services.image_records.get_most_recent_image_for_board(board_record.board_id)
|
||||
if cover_image:
|
||||
cover_image_name = cover_image.image_name
|
||||
else:
|
||||
cover_image_name = None
|
||||
image_count = self.__invoker.services.board_image_records.get_image_count_for_board(board_id)
|
||||
return board_record_to_dto(board_record, cover_image_name, image_count)
|
||||
|
||||
def update(
|
||||
self,
|
||||
@@ -28,7 +34,14 @@ class BoardService(BoardServiceABC):
|
||||
changes: BoardChanges,
|
||||
) -> BoardDTO:
|
||||
board_record = self.__invoker.services.board_records.update(board_id, changes)
|
||||
return BoardDTO.model_validate(board_record.model_dump())
|
||||
cover_image = self.__invoker.services.image_records.get_most_recent_image_for_board(board_record.board_id)
|
||||
if cover_image:
|
||||
cover_image_name = cover_image.image_name
|
||||
else:
|
||||
cover_image_name = None
|
||||
|
||||
image_count = self.__invoker.services.board_image_records.get_image_count_for_board(board_id)
|
||||
return board_record_to_dto(board_record, cover_image_name, image_count)
|
||||
|
||||
def delete(self, board_id: str) -> None:
|
||||
self.__invoker.services.board_records.delete(board_id)
|
||||
@@ -37,10 +50,30 @@ class BoardService(BoardServiceABC):
|
||||
self, offset: int = 0, limit: int = 10, include_archived: bool = False
|
||||
) -> OffsetPaginatedResults[BoardDTO]:
|
||||
board_records = self.__invoker.services.board_records.get_many(offset, limit, include_archived)
|
||||
board_dtos = [BoardDTO.model_validate(r.model_dump()) for r in board_records.items]
|
||||
board_dtos = []
|
||||
for r in board_records.items:
|
||||
cover_image = self.__invoker.services.image_records.get_most_recent_image_for_board(r.board_id)
|
||||
if cover_image:
|
||||
cover_image_name = cover_image.image_name
|
||||
else:
|
||||
cover_image_name = None
|
||||
|
||||
image_count = self.__invoker.services.board_image_records.get_image_count_for_board(r.board_id)
|
||||
board_dtos.append(board_record_to_dto(r, cover_image_name, image_count))
|
||||
|
||||
return OffsetPaginatedResults[BoardDTO](items=board_dtos, offset=offset, limit=limit, total=len(board_dtos))
|
||||
|
||||
def get_all(self, include_archived: bool = False) -> list[BoardDTO]:
|
||||
board_records = self.__invoker.services.board_records.get_all(include_archived)
|
||||
board_dtos = [BoardDTO.model_validate(r.model_dump()) for r in board_records]
|
||||
board_dtos = []
|
||||
for r in board_records:
|
||||
cover_image = self.__invoker.services.image_records.get_most_recent_image_for_board(r.board_id)
|
||||
if cover_image:
|
||||
cover_image_name = cover_image.image_name
|
||||
else:
|
||||
cover_image_name = None
|
||||
|
||||
image_count = self.__invoker.services.board_image_records.get_image_count_for_board(r.board_id)
|
||||
board_dtos.append(board_record_to_dto(r, cover_image_name, image_count))
|
||||
|
||||
return board_dtos
|
||||
|
||||
@@ -124,16 +124,14 @@ class IPAdapter(RawModel):
|
||||
self.device, dtype=self.dtype
|
||||
)
|
||||
|
||||
def to(
|
||||
self, device: Optional[torch.device] = None, dtype: Optional[torch.dtype] = None, non_blocking: bool = False
|
||||
):
|
||||
def to(self, device: Optional[torch.device] = None, dtype: Optional[torch.dtype] = None):
|
||||
if device is not None:
|
||||
self.device = device
|
||||
if dtype is not None:
|
||||
self.dtype = dtype
|
||||
|
||||
self._image_proj_model.to(device=self.device, dtype=self.dtype, non_blocking=non_blocking)
|
||||
self.attn_weights.to(device=self.device, dtype=self.dtype, non_blocking=non_blocking)
|
||||
self._image_proj_model.to(device=self.device, dtype=self.dtype)
|
||||
self.attn_weights.to(device=self.device, dtype=self.dtype)
|
||||
|
||||
def calc_size(self) -> int:
|
||||
# HACK(ryand): Fix this issue with circular imports.
|
||||
|
||||
@@ -11,7 +11,6 @@ from typing_extensions import Self
|
||||
|
||||
from invokeai.backend.model_manager import BaseModelType
|
||||
from invokeai.backend.raw_model import RawModel
|
||||
from invokeai.backend.util.devices import TorchDevice
|
||||
|
||||
|
||||
class LoRALayerBase:
|
||||
@@ -57,14 +56,9 @@ class LoRALayerBase:
|
||||
model_size += val.nelement() * val.element_size()
|
||||
return model_size
|
||||
|
||||
def to(
|
||||
self,
|
||||
device: Optional[torch.device] = None,
|
||||
dtype: Optional[torch.dtype] = None,
|
||||
non_blocking: bool = False,
|
||||
) -> None:
|
||||
def to(self, device: Optional[torch.device] = None, dtype: Optional[torch.dtype] = None) -> None:
|
||||
if self.bias is not None:
|
||||
self.bias = self.bias.to(device=device, dtype=dtype, non_blocking=non_blocking)
|
||||
self.bias = self.bias.to(device=device, dtype=dtype)
|
||||
|
||||
|
||||
# TODO: find and debug lora/locon with bias
|
||||
@@ -106,19 +100,14 @@ class LoRALayer(LoRALayerBase):
|
||||
model_size += val.nelement() * val.element_size()
|
||||
return model_size
|
||||
|
||||
def to(
|
||||
self,
|
||||
device: Optional[torch.device] = None,
|
||||
dtype: Optional[torch.dtype] = None,
|
||||
non_blocking: bool = False,
|
||||
) -> None:
|
||||
super().to(device=device, dtype=dtype, non_blocking=non_blocking)
|
||||
def to(self, device: Optional[torch.device] = None, dtype: Optional[torch.dtype] = None) -> None:
|
||||
super().to(device=device, dtype=dtype)
|
||||
|
||||
self.up = self.up.to(device=device, dtype=dtype, non_blocking=non_blocking)
|
||||
self.down = self.down.to(device=device, dtype=dtype, non_blocking=non_blocking)
|
||||
self.up = self.up.to(device=device, dtype=dtype)
|
||||
self.down = self.down.to(device=device, dtype=dtype)
|
||||
|
||||
if self.mid is not None:
|
||||
self.mid = self.mid.to(device=device, dtype=dtype, non_blocking=non_blocking)
|
||||
self.mid = self.mid.to(device=device, dtype=dtype)
|
||||
|
||||
|
||||
class LoHALayer(LoRALayerBase):
|
||||
@@ -167,23 +156,18 @@ class LoHALayer(LoRALayerBase):
|
||||
model_size += val.nelement() * val.element_size()
|
||||
return model_size
|
||||
|
||||
def to(
|
||||
self,
|
||||
device: Optional[torch.device] = None,
|
||||
dtype: Optional[torch.dtype] = None,
|
||||
non_blocking: bool = False,
|
||||
) -> None:
|
||||
def to(self, device: Optional[torch.device] = None, dtype: Optional[torch.dtype] = None) -> None:
|
||||
super().to(device=device, dtype=dtype)
|
||||
|
||||
self.w1_a = self.w1_a.to(device=device, dtype=dtype, non_blocking=non_blocking)
|
||||
self.w1_b = self.w1_b.to(device=device, dtype=dtype, non_blocking=non_blocking)
|
||||
self.w1_a = self.w1_a.to(device=device, dtype=dtype)
|
||||
self.w1_b = self.w1_b.to(device=device, dtype=dtype)
|
||||
if self.t1 is not None:
|
||||
self.t1 = self.t1.to(device=device, dtype=dtype, non_blocking=non_blocking)
|
||||
self.t1 = self.t1.to(device=device, dtype=dtype)
|
||||
|
||||
self.w2_a = self.w2_a.to(device=device, dtype=dtype, non_blocking=non_blocking)
|
||||
self.w2_b = self.w2_b.to(device=device, dtype=dtype, non_blocking=non_blocking)
|
||||
self.w2_a = self.w2_a.to(device=device, dtype=dtype)
|
||||
self.w2_b = self.w2_b.to(device=device, dtype=dtype)
|
||||
if self.t2 is not None:
|
||||
self.t2 = self.t2.to(device=device, dtype=dtype, non_blocking=non_blocking)
|
||||
self.t2 = self.t2.to(device=device, dtype=dtype)
|
||||
|
||||
|
||||
class LoKRLayer(LoRALayerBase):
|
||||
@@ -264,12 +248,7 @@ class LoKRLayer(LoRALayerBase):
|
||||
model_size += val.nelement() * val.element_size()
|
||||
return model_size
|
||||
|
||||
def to(
|
||||
self,
|
||||
device: Optional[torch.device] = None,
|
||||
dtype: Optional[torch.dtype] = None,
|
||||
non_blocking: bool = False,
|
||||
) -> None:
|
||||
def to(self, device: Optional[torch.device] = None, dtype: Optional[torch.dtype] = None) -> None:
|
||||
super().to(device=device, dtype=dtype)
|
||||
|
||||
if self.w1 is not None:
|
||||
@@ -277,19 +256,19 @@ class LoKRLayer(LoRALayerBase):
|
||||
else:
|
||||
assert self.w1_a is not None
|
||||
assert self.w1_b is not None
|
||||
self.w1_a = self.w1_a.to(device=device, dtype=dtype, non_blocking=non_blocking)
|
||||
self.w1_b = self.w1_b.to(device=device, dtype=dtype, non_blocking=non_blocking)
|
||||
self.w1_a = self.w1_a.to(device=device, dtype=dtype)
|
||||
self.w1_b = self.w1_b.to(device=device, dtype=dtype)
|
||||
|
||||
if self.w2 is not None:
|
||||
self.w2 = self.w2.to(device=device, dtype=dtype, non_blocking=non_blocking)
|
||||
self.w2 = self.w2.to(device=device, dtype=dtype)
|
||||
else:
|
||||
assert self.w2_a is not None
|
||||
assert self.w2_b is not None
|
||||
self.w2_a = self.w2_a.to(device=device, dtype=dtype, non_blocking=non_blocking)
|
||||
self.w2_b = self.w2_b.to(device=device, dtype=dtype, non_blocking=non_blocking)
|
||||
self.w2_a = self.w2_a.to(device=device, dtype=dtype)
|
||||
self.w2_b = self.w2_b.to(device=device, dtype=dtype)
|
||||
|
||||
if self.t2 is not None:
|
||||
self.t2 = self.t2.to(device=device, dtype=dtype, non_blocking=non_blocking)
|
||||
self.t2 = self.t2.to(device=device, dtype=dtype)
|
||||
|
||||
|
||||
class FullLayer(LoRALayerBase):
|
||||
@@ -319,15 +298,10 @@ class FullLayer(LoRALayerBase):
|
||||
model_size += self.weight.nelement() * self.weight.element_size()
|
||||
return model_size
|
||||
|
||||
def to(
|
||||
self,
|
||||
device: Optional[torch.device] = None,
|
||||
dtype: Optional[torch.dtype] = None,
|
||||
non_blocking: bool = False,
|
||||
) -> None:
|
||||
def to(self, device: Optional[torch.device] = None, dtype: Optional[torch.dtype] = None) -> None:
|
||||
super().to(device=device, dtype=dtype)
|
||||
|
||||
self.weight = self.weight.to(device=device, dtype=dtype, non_blocking=non_blocking)
|
||||
self.weight = self.weight.to(device=device, dtype=dtype)
|
||||
|
||||
|
||||
class IA3Layer(LoRALayerBase):
|
||||
@@ -359,16 +333,11 @@ class IA3Layer(LoRALayerBase):
|
||||
model_size += self.on_input.nelement() * self.on_input.element_size()
|
||||
return model_size
|
||||
|
||||
def to(
|
||||
self,
|
||||
device: Optional[torch.device] = None,
|
||||
dtype: Optional[torch.dtype] = None,
|
||||
non_blocking: bool = False,
|
||||
):
|
||||
def to(self, device: Optional[torch.device] = None, dtype: Optional[torch.dtype] = None):
|
||||
super().to(device=device, dtype=dtype)
|
||||
|
||||
self.weight = self.weight.to(device=device, dtype=dtype, non_blocking=non_blocking)
|
||||
self.on_input = self.on_input.to(device=device, dtype=dtype, non_blocking=non_blocking)
|
||||
self.weight = self.weight.to(device=device, dtype=dtype)
|
||||
self.on_input = self.on_input.to(device=device, dtype=dtype)
|
||||
|
||||
|
||||
AnyLoRALayer = Union[LoRALayer, LoHALayer, LoKRLayer, FullLayer, IA3Layer]
|
||||
@@ -390,15 +359,10 @@ class LoRAModelRaw(RawModel): # (torch.nn.Module):
|
||||
def name(self) -> str:
|
||||
return self._name
|
||||
|
||||
def to(
|
||||
self,
|
||||
device: Optional[torch.device] = None,
|
||||
dtype: Optional[torch.dtype] = None,
|
||||
non_blocking: bool = False,
|
||||
) -> None:
|
||||
def to(self, device: Optional[torch.device] = None, dtype: Optional[torch.dtype] = None) -> None:
|
||||
# TODO: try revert if exception?
|
||||
for _key, layer in self.layers.items():
|
||||
layer.to(device=device, dtype=dtype, non_blocking=non_blocking)
|
||||
layer.to(device=device, dtype=dtype)
|
||||
|
||||
def calc_size(self) -> int:
|
||||
model_size = 0
|
||||
@@ -521,7 +485,7 @@ class LoRAModelRaw(RawModel): # (torch.nn.Module):
|
||||
# lower memory consumption by removing already parsed layer values
|
||||
state_dict[layer_key].clear()
|
||||
|
||||
layer.to(device=device, dtype=dtype, non_blocking=TorchDevice.get_non_blocking(device))
|
||||
layer.to(device=device, dtype=dtype)
|
||||
model.layers[layer_key] = layer
|
||||
|
||||
return model
|
||||
|
||||
@@ -289,11 +289,9 @@ class ModelCache(ModelCacheBase[AnyModel]):
|
||||
else:
|
||||
new_dict: Dict[str, torch.Tensor] = {}
|
||||
for k, v in cache_entry.state_dict.items():
|
||||
new_dict[k] = v.to(
|
||||
target_device, copy=True, non_blocking=TorchDevice.get_non_blocking(target_device)
|
||||
)
|
||||
new_dict[k] = v.to(target_device, copy=True)
|
||||
cache_entry.model.load_state_dict(new_dict, assign=True)
|
||||
cache_entry.model.to(target_device, non_blocking=TorchDevice.get_non_blocking(target_device))
|
||||
cache_entry.model.to(target_device)
|
||||
cache_entry.device = target_device
|
||||
except Exception as e: # blow away cache entry
|
||||
self._delete_cache_entry(cache_entry)
|
||||
|
||||
@@ -139,15 +139,12 @@ class ModelPatcher:
|
||||
# We intentionally move to the target device first, then cast. Experimentally, this was found to
|
||||
# be significantly faster for 16-bit CPU tensors being moved to a CUDA device than doing the
|
||||
# same thing in a single call to '.to(...)'.
|
||||
layer.to(device=device, non_blocking=TorchDevice.get_non_blocking(device))
|
||||
layer.to(dtype=torch.float32, non_blocking=TorchDevice.get_non_blocking(device))
|
||||
layer.to(device=device)
|
||||
layer.to(dtype=torch.float32)
|
||||
# TODO(ryand): Using torch.autocast(...) over explicit casting may offer a speed benefit on CUDA
|
||||
# devices here. Experimentally, it was found to be very slow on CPU. More investigation needed.
|
||||
layer_weight = layer.get_weight(module.weight) * (lora_weight * layer_scale)
|
||||
layer.to(
|
||||
device=TorchDevice.CPU_DEVICE,
|
||||
non_blocking=TorchDevice.get_non_blocking(TorchDevice.CPU_DEVICE),
|
||||
)
|
||||
layer.to(device=TorchDevice.CPU_DEVICE)
|
||||
|
||||
assert isinstance(layer_weight, torch.Tensor) # mypy thinks layer_weight is a float|Any ??!
|
||||
if module.weight.shape != layer_weight.shape:
|
||||
@@ -156,7 +153,7 @@ class ModelPatcher:
|
||||
layer_weight = layer_weight.reshape(module.weight.shape)
|
||||
|
||||
assert isinstance(layer_weight, torch.Tensor) # mypy thinks layer_weight is a float|Any ??!
|
||||
module.weight += layer_weight.to(dtype=dtype, non_blocking=TorchDevice.get_non_blocking(device))
|
||||
module.weight += layer_weight.to(dtype=dtype)
|
||||
|
||||
yield # wait for context manager exit
|
||||
|
||||
@@ -164,9 +161,7 @@ class ModelPatcher:
|
||||
assert hasattr(model, "get_submodule") # mypy not picking up fact that torch.nn.Module has get_submodule()
|
||||
with torch.no_grad():
|
||||
for module_key, weight in original_weights.items():
|
||||
model.get_submodule(module_key).weight.copy_(
|
||||
weight, non_blocking=TorchDevice.get_non_blocking(weight.device)
|
||||
)
|
||||
model.get_submodule(module_key).weight.copy_(weight)
|
||||
|
||||
@classmethod
|
||||
@contextmanager
|
||||
|
||||
@@ -190,12 +190,7 @@ class IAIOnnxRuntimeModel(RawModel):
|
||||
return self.session.run(None, inputs)
|
||||
|
||||
# compatability with RawModel ABC
|
||||
def to(
|
||||
self,
|
||||
device: Optional[torch.device] = None,
|
||||
dtype: Optional[torch.dtype] = None,
|
||||
non_blocking: bool = False,
|
||||
) -> None:
|
||||
def to(self, device: Optional[torch.device] = None, dtype: Optional[torch.dtype] = None) -> None:
|
||||
pass
|
||||
|
||||
# compatability with diffusers load code
|
||||
|
||||
@@ -20,10 +20,5 @@ class RawModel(ABC):
|
||||
"""Abstract base class for 'Raw' model wrappers."""
|
||||
|
||||
@abstractmethod
|
||||
def to(
|
||||
self,
|
||||
device: Optional[torch.device] = None,
|
||||
dtype: Optional[torch.dtype] = None,
|
||||
non_blocking: bool = False,
|
||||
) -> None:
|
||||
def to(self, device: Optional[torch.device] = None, dtype: Optional[torch.dtype] = None) -> None:
|
||||
pass
|
||||
|
||||
@@ -65,17 +65,12 @@ class TextualInversionModelRaw(RawModel):
|
||||
|
||||
return result
|
||||
|
||||
def to(
|
||||
self,
|
||||
device: Optional[torch.device] = None,
|
||||
dtype: Optional[torch.dtype] = None,
|
||||
non_blocking: bool = False,
|
||||
) -> None:
|
||||
def to(self, device: Optional[torch.device] = None, dtype: Optional[torch.dtype] = None) -> None:
|
||||
if not torch.cuda.is_available():
|
||||
return
|
||||
for emb in [self.embedding, self.embedding_2]:
|
||||
if emb is not None:
|
||||
emb.to(device=device, dtype=dtype, non_blocking=non_blocking)
|
||||
emb.to(device=device, dtype=dtype)
|
||||
|
||||
def calc_size(self) -> int:
|
||||
"""Get the size of this model in bytes."""
|
||||
|
||||
@@ -112,15 +112,3 @@ class TorchDevice:
|
||||
@classmethod
|
||||
def _to_dtype(cls, precision_name: TorchPrecisionNames) -> torch.dtype:
|
||||
return NAME_TO_PRECISION[precision_name]
|
||||
|
||||
@staticmethod
|
||||
def get_non_blocking(to_device: torch.device) -> bool:
|
||||
"""Return the non_blocking flag to be used when moving a tensor to a given device.
|
||||
MPS may have unexpected errors with non-blocking operations - we should not use non-blocking when moving _to_ MPS.
|
||||
When moving _from_ MPS, we can use non-blocking operations.
|
||||
|
||||
See:
|
||||
- https://github.com/pytorch/pytorch/issues/107455
|
||||
- https://discuss.pytorch.org/t/should-we-set-non-blocking-to-true/38234/28
|
||||
"""
|
||||
return False if to_device.type == "mps" else True
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
import { $nodeExecutionStates, upsertExecutionState } from 'features/nodes/hooks/useExecutionState';
|
||||
import { zNodeStatus } from 'features/nodes/types/invocation';
|
||||
import { CANVAS_OUTPUT } from 'features/nodes/util/graph/constants';
|
||||
import { boardsApi } from 'services/api/endpoints/boards';
|
||||
import { imagesApi } from 'services/api/endpoints/images';
|
||||
import { getCategories, getListImagesUrl } from 'services/api/util';
|
||||
import { socketInvocationComplete } from 'services/events/actions';
|
||||
@@ -51,6 +52,14 @@ export const addInvocationCompleteEventListener = (startAppListening: AppStartLi
|
||||
}
|
||||
|
||||
if (!imageDTO.is_intermediate) {
|
||||
// update the total images for the board
|
||||
dispatch(
|
||||
boardsApi.util.updateQueryData('getBoardImagesTotal', imageDTO.board_id ?? 'none', (draft) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
draft.total += 1;
|
||||
})
|
||||
);
|
||||
|
||||
dispatch(
|
||||
imagesApi.util.invalidateTags([
|
||||
{ type: 'Board', id: imageDTO.board_id ?? 'none' },
|
||||
|
||||
@@ -1,12 +1,22 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useGetBoardAssetsTotalQuery, useGetBoardImagesTotalQuery } from 'services/api/endpoints/boards';
|
||||
|
||||
type Props = {
|
||||
imageCount: number;
|
||||
assetCount: number;
|
||||
board_id: string;
|
||||
isArchived: boolean;
|
||||
};
|
||||
|
||||
export const BoardTotalsTooltip = ({ imageCount, assetCount, isArchived }: Props) => {
|
||||
export const BoardTotalsTooltip = ({ board_id, isArchived }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
return `${t('boards.imagesWithCount', { count: imageCount })}, ${t('boards.assetsWithCount', { count: assetCount })}${isArchived ? ` (${t('boards.archived')})` : ''}`;
|
||||
const { imagesTotal } = useGetBoardImagesTotalQuery(board_id, {
|
||||
selectFromResult: ({ data }) => {
|
||||
return { imagesTotal: data?.total ?? 0 };
|
||||
},
|
||||
});
|
||||
const { assetsTotal } = useGetBoardAssetsTotalQuery(board_id, {
|
||||
selectFromResult: ({ data }) => {
|
||||
return { assetsTotal: data?.total ?? 0 };
|
||||
},
|
||||
});
|
||||
return `${t('boards.imagesWithCount', { count: imagesTotal })}, ${t('boards.assetsWithCount', { count: assetsTotal })}${isArchived ? ` (${t('boards.archived')})` : ''}`;
|
||||
};
|
||||
|
||||
@@ -116,13 +116,7 @@ const GalleryBoard = ({ board, isSelected, setBoardToDelete }: GalleryBoardProps
|
||||
<BoardContextMenu board={board} setBoardToDelete={setBoardToDelete}>
|
||||
{(ref) => (
|
||||
<Tooltip
|
||||
label={
|
||||
<BoardTotalsTooltip
|
||||
imageCount={board.image_count}
|
||||
assetCount={board.asset_count}
|
||||
isArchived={Boolean(board.archived)}
|
||||
/>
|
||||
}
|
||||
label={<BoardTotalsTooltip board_id={board.board_id} isArchived={Boolean(board.archived)} />}
|
||||
openDelay={1000}
|
||||
placement="left"
|
||||
closeOnScroll
|
||||
@@ -172,7 +166,7 @@ const GalleryBoard = ({ board, isSelected, setBoardToDelete }: GalleryBoardProps
|
||||
</Editable>
|
||||
{autoAddBoardId === board.board_id && !editingDisclosure.isOpen && <AutoAddBadge />}
|
||||
{board.archived && !editingDisclosure.isOpen && <Icon as={PiArchiveBold} fill="base.300" />}
|
||||
{!editingDisclosure.isOpen && <Text variant="subtext">{board.image_count + board.asset_count}</Text>}
|
||||
{!editingDisclosure.isOpen && <Text variant="subtext">{board.image_count}</Text>}
|
||||
|
||||
<IAIDroppable data={droppableData} dropLabel={<Text fontSize="md">{t('unifiedCanvas.move')}</Text>} />
|
||||
</Flex>
|
||||
|
||||
@@ -9,7 +9,7 @@ import NoBoardBoardContextMenu from 'features/gallery/components/Boards/NoBoardB
|
||||
import { autoAddBoardIdChanged, boardIdSelected } from 'features/gallery/store/gallerySlice';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useGetUncategorizedImageCountsQuery } from 'services/api/endpoints/boards';
|
||||
import { useGetBoardImagesTotalQuery } from 'services/api/endpoints/boards';
|
||||
import { useBoardName } from 'services/api/hooks/useBoardName';
|
||||
|
||||
interface Props {
|
||||
@@ -22,7 +22,11 @@ const _hover: SystemStyleObject = {
|
||||
|
||||
const NoBoardBoard = memo(({ isSelected }: Props) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { data } = useGetUncategorizedImageCountsQuery();
|
||||
const { imagesTotal } = useGetBoardImagesTotalQuery('none', {
|
||||
selectFromResult: ({ data }) => {
|
||||
return { imagesTotal: data?.total ?? 0 };
|
||||
},
|
||||
});
|
||||
const autoAddBoardId = useAppSelector((s) => s.gallery.autoAddBoardId);
|
||||
const autoAssignBoardOnClick = useAppSelector((s) => s.gallery.autoAssignBoardOnClick);
|
||||
const boardSearchText = useAppSelector((s) => s.gallery.boardSearchText);
|
||||
@@ -56,13 +60,7 @@ const NoBoardBoard = memo(({ isSelected }: Props) => {
|
||||
<NoBoardBoardContextMenu>
|
||||
{(ref) => (
|
||||
<Tooltip
|
||||
label={
|
||||
<BoardTotalsTooltip
|
||||
imageCount={data?.image_count ?? 0}
|
||||
assetCount={data?.asset_count ?? 0}
|
||||
isArchived={false}
|
||||
/>
|
||||
}
|
||||
label={<BoardTotalsTooltip board_id="none" isArchived={false} />}
|
||||
openDelay={1000}
|
||||
placement="left"
|
||||
closeOnScroll
|
||||
@@ -101,7 +99,7 @@ const NoBoardBoard = memo(({ isSelected }: Props) => {
|
||||
{boardName}
|
||||
</Text>
|
||||
{autoAddBoardId === 'none' && <AutoAddBadge />}
|
||||
<Text variant="subtext">{(data?.image_count ?? 0) + (data?.asset_count ?? 0)}</Text>
|
||||
<Text variant="subtext">{imagesTotal}</Text>
|
||||
<IAIDroppable data={droppableData} dropLabel={<Text fontSize="md">{t('unifiedCanvas.move')}</Text>} />
|
||||
</Flex>
|
||||
</Tooltip>
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
import type { BoardDTO, CreateBoardArg, ListBoardsArgs, S, UpdateBoardArg } from 'services/api/types';
|
||||
import { ASSETS_CATEGORIES, IMAGE_CATEGORIES } from 'features/gallery/store/types';
|
||||
import type {
|
||||
BoardDTO,
|
||||
CreateBoardArg,
|
||||
ListBoardsArgs,
|
||||
OffsetPaginatedResults_ImageDTO_,
|
||||
UpdateBoardArg,
|
||||
} from 'services/api/types';
|
||||
import { getListImagesUrl } from 'services/api/util';
|
||||
|
||||
import type { ApiTagDescription } from '..';
|
||||
import { api, buildV1Url, LIST_TAG } from '..';
|
||||
@@ -47,11 +55,38 @@ export const boardsApi = api.injectEndpoints({
|
||||
keepUnusedDataFor: 0,
|
||||
}),
|
||||
|
||||
getUncategorizedImageCounts: build.query<S['UncategorizedImageCounts'], void>({
|
||||
query: () => ({
|
||||
url: buildBoardsUrl('uncategorized/counts'),
|
||||
getBoardImagesTotal: build.query<{ total: number }, string | undefined>({
|
||||
query: (board_id) => ({
|
||||
url: getListImagesUrl({
|
||||
board_id: board_id ?? 'none',
|
||||
categories: IMAGE_CATEGORIES,
|
||||
is_intermediate: false,
|
||||
limit: 0,
|
||||
offset: 0,
|
||||
}),
|
||||
method: 'GET',
|
||||
}),
|
||||
providesTags: ['UncategorizedImageCounts', { type: 'Board', id: LIST_TAG }, { type: 'Board', id: 'none' }],
|
||||
providesTags: (result, error, arg) => [{ type: 'BoardImagesTotal', id: arg ?? 'none' }, 'FetchOnReconnect'],
|
||||
transformResponse: (response: OffsetPaginatedResults_ImageDTO_) => {
|
||||
return { total: response.total };
|
||||
},
|
||||
}),
|
||||
|
||||
getBoardAssetsTotal: build.query<{ total: number }, string | undefined>({
|
||||
query: (board_id) => ({
|
||||
url: getListImagesUrl({
|
||||
board_id: board_id ?? 'none',
|
||||
categories: ASSETS_CATEGORIES,
|
||||
is_intermediate: false,
|
||||
limit: 0,
|
||||
offset: 0,
|
||||
}),
|
||||
method: 'GET',
|
||||
}),
|
||||
providesTags: (result, error, arg) => [{ type: 'BoardAssetsTotal', id: arg ?? 'none' }, 'FetchOnReconnect'],
|
||||
transformResponse: (response: OffsetPaginatedResults_ImageDTO_) => {
|
||||
return { total: response.total };
|
||||
},
|
||||
}),
|
||||
|
||||
/**
|
||||
@@ -89,8 +124,9 @@ export const boardsApi = api.injectEndpoints({
|
||||
|
||||
export const {
|
||||
useListAllBoardsQuery,
|
||||
useGetBoardImagesTotalQuery,
|
||||
useGetBoardAssetsTotalQuery,
|
||||
useCreateBoardMutation,
|
||||
useUpdateBoardMutation,
|
||||
useListAllImageNamesForBoardQuery,
|
||||
useGetUncategorizedImageCountsQuery,
|
||||
} = boardsApi;
|
||||
|
||||
@@ -44,7 +44,6 @@ const tagTypes = [
|
||||
// This is invalidated on reconnect. It should be used for queries that have changing data,
|
||||
// especially related to the queue and generation.
|
||||
'FetchOnReconnect',
|
||||
'UncategorizedImageCounts',
|
||||
] as const;
|
||||
export type ApiTagDescription = TagDescription<(typeof tagTypes)[number]>;
|
||||
export const LIST_TAG = 'LIST';
|
||||
|
||||
@@ -333,13 +333,6 @@ export type paths = {
|
||||
*/
|
||||
get: operations["list_all_board_image_names"];
|
||||
};
|
||||
"/api/v1/boards/uncategorized/counts": {
|
||||
/**
|
||||
* Get Uncategorized Image Counts
|
||||
* @description Gets count of images and assets for uncategorized images (images with no board assocation)
|
||||
*/
|
||||
get: operations["get_uncategorized_image_counts"];
|
||||
};
|
||||
"/api/v1/board_images/": {
|
||||
/**
|
||||
* Add Image To Board
|
||||
@@ -1027,7 +1020,7 @@ export type components = {
|
||||
};
|
||||
/**
|
||||
* BoardDTO
|
||||
* @description Deserialized board record.
|
||||
* @description Deserialized board record with cover image URL and image count.
|
||||
*/
|
||||
BoardDTO: {
|
||||
/**
|
||||
@@ -1057,9 +1050,9 @@ export type components = {
|
||||
deleted_at?: string | null;
|
||||
/**
|
||||
* Cover Image Name
|
||||
* @description The name of the cover image of the board.
|
||||
* @description The name of the board's cover image.
|
||||
*/
|
||||
cover_image_name?: string | null;
|
||||
cover_image_name: string | null;
|
||||
/**
|
||||
* Archived
|
||||
* @description Whether or not the board is archived.
|
||||
@@ -1075,11 +1068,6 @@ export type components = {
|
||||
* @description The number of images in the board.
|
||||
*/
|
||||
image_count: number;
|
||||
/**
|
||||
* Asset Count
|
||||
* @description The number of assets in the board.
|
||||
*/
|
||||
asset_count: number;
|
||||
};
|
||||
/**
|
||||
* BoardField
|
||||
@@ -7316,145 +7304,145 @@ export type components = {
|
||||
project_id: string | null;
|
||||
};
|
||||
InvocationOutputMap: {
|
||||
img_channel_offset: components["schemas"]["ImageOutput"];
|
||||
metadata: components["schemas"]["MetadataOutput"];
|
||||
clip_skip: components["schemas"]["CLIPSkipInvocationOutput"];
|
||||
canvas_paste_back: components["schemas"]["ImageOutput"];
|
||||
seamless: components["schemas"]["SeamlessModeOutput"];
|
||||
blank_image: components["schemas"]["ImageOutput"];
|
||||
dynamic_prompt: components["schemas"]["StringCollectionOutput"];
|
||||
step_param_easing: components["schemas"]["FloatCollectionOutput"];
|
||||
latents_collection: components["schemas"]["LatentsCollectionOutput"];
|
||||
normalbae_image_processor: components["schemas"]["ImageOutput"];
|
||||
rand_float: components["schemas"]["FloatOutput"];
|
||||
lora_loader: components["schemas"]["LoRALoaderOutput"];
|
||||
collect: components["schemas"]["CollectInvocationOutput"];
|
||||
infill_rgba: components["schemas"]["ImageOutput"];
|
||||
img_lerp: components["schemas"]["ImageOutput"];
|
||||
integer_math: components["schemas"]["IntegerOutput"];
|
||||
conditioning_collection: components["schemas"]["ConditioningCollectionOutput"];
|
||||
mask_from_id: components["schemas"]["ImageOutput"];
|
||||
mlsd_image_processor: components["schemas"]["ImageOutput"];
|
||||
zoe_depth_image_processor: components["schemas"]["ImageOutput"];
|
||||
ideal_size: components["schemas"]["IdealSizeOutput"];
|
||||
conditioning: components["schemas"]["ConditioningOutput"];
|
||||
img_resize: components["schemas"]["ImageOutput"];
|
||||
integer_collection: components["schemas"]["IntegerCollectionOutput"];
|
||||
float_range: components["schemas"]["FloatCollectionOutput"];
|
||||
tile_to_properties: components["schemas"]["TileToPropertiesOutput"];
|
||||
alpha_mask_to_tensor: components["schemas"]["MaskOutput"];
|
||||
img_watermark: components["schemas"]["ImageOutput"];
|
||||
merge_tiles_to_image: components["schemas"]["ImageOutput"];
|
||||
merge_metadata: components["schemas"]["MetadataOutput"];
|
||||
round_float: components["schemas"]["FloatOutput"];
|
||||
denoise_latents: components["schemas"]["LatentsOutput"];
|
||||
string_join_three: components["schemas"]["StringOutput"];
|
||||
img_blur: components["schemas"]["ImageOutput"];
|
||||
color_map_image_processor: components["schemas"]["ImageOutput"];
|
||||
img_scale: components["schemas"]["ImageOutput"];
|
||||
infill_tile: components["schemas"]["ImageOutput"];
|
||||
add: components["schemas"]["IntegerOutput"];
|
||||
img_paste: components["schemas"]["ImageOutput"];
|
||||
img_crop: components["schemas"]["ImageOutput"];
|
||||
cv_inpaint: components["schemas"]["ImageOutput"];
|
||||
image_collection: components["schemas"]["ImageCollectionOutput"];
|
||||
img_pad_crop: components["schemas"]["ImageOutput"];
|
||||
canny_image_processor: components["schemas"]["ImageOutput"];
|
||||
model_identifier: components["schemas"]["ModelIdentifierOutput"];
|
||||
i2l: components["schemas"]["LatentsOutput"];
|
||||
face_mask_detection: components["schemas"]["FaceMaskOutput"];
|
||||
img_channel_multiply: components["schemas"]["ImageOutput"];
|
||||
sdxl_model_loader: components["schemas"]["SDXLModelLoaderOutput"];
|
||||
img_mul: components["schemas"]["ImageOutput"];
|
||||
tomask: components["schemas"]["ImageOutput"];
|
||||
image_mask_to_tensor: components["schemas"]["MaskOutput"];
|
||||
face_identifier: components["schemas"]["ImageOutput"];
|
||||
noise: components["schemas"]["NoiseOutput"];
|
||||
l2i: components["schemas"]["ImageOutput"];
|
||||
mul: components["schemas"]["IntegerOutput"];
|
||||
sub: components["schemas"]["IntegerOutput"];
|
||||
main_model_loader: components["schemas"]["ModelLoaderOutput"];
|
||||
controlnet: components["schemas"]["ControlOutput"];
|
||||
ip_adapter: components["schemas"]["IPAdapterOutput"];
|
||||
lscale: components["schemas"]["LatentsOutput"];
|
||||
sdxl_lora_collection_loader: components["schemas"]["SDXLLoRALoaderOutput"];
|
||||
latents: components["schemas"]["LatentsOutput"];
|
||||
string_split: components["schemas"]["String2Output"];
|
||||
sdxl_refiner_compel_prompt: components["schemas"]["ConditioningOutput"];
|
||||
esrgan: components["schemas"]["ImageOutput"];
|
||||
dw_openpose_image_processor: components["schemas"]["ImageOutput"];
|
||||
compel: components["schemas"]["ConditioningOutput"];
|
||||
sdxl_lora_loader: components["schemas"]["SDXLLoRALoaderOutput"];
|
||||
sdxl_compel_prompt: components["schemas"]["ConditioningOutput"];
|
||||
tile_image_processor: components["schemas"]["ImageOutput"];
|
||||
mediapipe_face_processor: components["schemas"]["ImageOutput"];
|
||||
metadata_item: components["schemas"]["MetadataItemOutput"];
|
||||
float_math: components["schemas"]["FloatOutput"];
|
||||
prompt_from_file: components["schemas"]["StringCollectionOutput"];
|
||||
pidi_image_processor: components["schemas"]["ImageOutput"];
|
||||
content_shuffle_image_processor: components["schemas"]["ImageOutput"];
|
||||
lineart_anime_image_processor: components["schemas"]["ImageOutput"];
|
||||
t2i_adapter: components["schemas"]["T2IAdapterOutput"];
|
||||
integer: components["schemas"]["IntegerOutput"];
|
||||
unsharp_mask: components["schemas"]["ImageOutput"];
|
||||
range: components["schemas"]["IntegerCollectionOutput"];
|
||||
string: components["schemas"]["StringOutput"];
|
||||
show_image: components["schemas"]["ImageOutput"];
|
||||
image: components["schemas"]["ImageOutput"];
|
||||
heuristic_resize: components["schemas"]["ImageOutput"];
|
||||
div: components["schemas"]["IntegerOutput"];
|
||||
rand_int: components["schemas"]["IntegerOutput"];
|
||||
float: components["schemas"]["FloatOutput"];
|
||||
img_conv: components["schemas"]["ImageOutput"];
|
||||
mask_combine: components["schemas"]["ImageOutput"];
|
||||
random_range: components["schemas"]["IntegerCollectionOutput"];
|
||||
boolean_collection: components["schemas"]["BooleanCollectionOutput"];
|
||||
pair_tile_image: components["schemas"]["PairTileImageOutput"];
|
||||
save_image: components["schemas"]["ImageOutput"];
|
||||
lora_selector: components["schemas"]["LoRASelectorOutput"];
|
||||
boolean: components["schemas"]["BooleanOutput"];
|
||||
tiled_multi_diffusion_denoise_latents: components["schemas"]["LatentsOutput"];
|
||||
rectangle_mask: components["schemas"]["MaskOutput"];
|
||||
lineart_image_processor: components["schemas"]["ImageOutput"];
|
||||
midas_depth_image_processor: components["schemas"]["ImageOutput"];
|
||||
img_nsfw: components["schemas"]["ImageOutput"];
|
||||
infill_patchmatch: components["schemas"]["ImageOutput"];
|
||||
infill_lama: components["schemas"]["ImageOutput"];
|
||||
infill_cv2: components["schemas"]["ImageOutput"];
|
||||
float_to_int: components["schemas"]["IntegerOutput"];
|
||||
color: components["schemas"]["ColorOutput"];
|
||||
lora_collection_loader: components["schemas"]["LoRALoaderOutput"];
|
||||
vae_loader: components["schemas"]["VAEOutput"];
|
||||
string_split_neg: components["schemas"]["StringPosNegOutput"];
|
||||
lresize: components["schemas"]["LatentsOutput"];
|
||||
string_collection: components["schemas"]["StringCollectionOutput"];
|
||||
invert_tensor_mask: components["schemas"]["MaskOutput"];
|
||||
depth_anything_image_processor: components["schemas"]["ImageOutput"];
|
||||
hed_image_processor: components["schemas"]["ImageOutput"];
|
||||
leres_image_processor: components["schemas"]["ImageOutput"];
|
||||
img_ilerp: components["schemas"]["ImageOutput"];
|
||||
freeu: components["schemas"]["UNetOutput"];
|
||||
mask_edge: components["schemas"]["ImageOutput"];
|
||||
string_join: components["schemas"]["StringOutput"];
|
||||
img_hue_adjust: components["schemas"]["ImageOutput"];
|
||||
color_correct: components["schemas"]["ImageOutput"];
|
||||
calculate_image_tiles_min_overlap: components["schemas"]["CalculateImageTilesOutput"];
|
||||
img_chan: components["schemas"]["ImageOutput"];
|
||||
calculate_image_tiles_even_split: components["schemas"]["CalculateImageTilesOutput"];
|
||||
create_denoise_mask: components["schemas"]["DenoiseMaskOutput"];
|
||||
lblend: components["schemas"]["LatentsOutput"];
|
||||
crop_latents: components["schemas"]["LatentsOutput"];
|
||||
string_replace: components["schemas"]["StringOutput"];
|
||||
range_of_size: components["schemas"]["IntegerCollectionOutput"];
|
||||
calculate_image_tiles: components["schemas"]["CalculateImageTilesOutput"];
|
||||
iterate: components["schemas"]["IterateInvocationOutput"];
|
||||
compel: components["schemas"]["ConditioningOutput"];
|
||||
img_resize: components["schemas"]["ImageOutput"];
|
||||
ideal_size: components["schemas"]["IdealSizeOutput"];
|
||||
rand_int: components["schemas"]["IntegerOutput"];
|
||||
clip_skip: components["schemas"]["CLIPSkipInvocationOutput"];
|
||||
string_collection: components["schemas"]["StringCollectionOutput"];
|
||||
create_gradient_mask: components["schemas"]["GradientMaskOutput"];
|
||||
face_off: components["schemas"]["FaceOffOutput"];
|
||||
sdxl_refiner_model_loader: components["schemas"]["SDXLRefinerModelLoaderOutput"];
|
||||
round_float: components["schemas"]["FloatOutput"];
|
||||
scheduler: components["schemas"]["SchedulerOutput"];
|
||||
float_collection: components["schemas"]["FloatCollectionOutput"];
|
||||
main_model_loader: components["schemas"]["ModelLoaderOutput"];
|
||||
string_split: components["schemas"]["String2Output"];
|
||||
mask_from_id: components["schemas"]["ImageOutput"];
|
||||
collect: components["schemas"]["CollectInvocationOutput"];
|
||||
heuristic_resize: components["schemas"]["ImageOutput"];
|
||||
tomask: components["schemas"]["ImageOutput"];
|
||||
boolean_collection: components["schemas"]["BooleanCollectionOutput"];
|
||||
core_metadata: components["schemas"]["MetadataOutput"];
|
||||
canny_image_processor: components["schemas"]["ImageOutput"];
|
||||
string_replace: components["schemas"]["StringOutput"];
|
||||
face_mask_detection: components["schemas"]["FaceMaskOutput"];
|
||||
integer: components["schemas"]["IntegerOutput"];
|
||||
img_watermark: components["schemas"]["ImageOutput"];
|
||||
img_crop: components["schemas"]["ImageOutput"];
|
||||
t2i_adapter: components["schemas"]["T2IAdapterOutput"];
|
||||
create_denoise_mask: components["schemas"]["DenoiseMaskOutput"];
|
||||
rand_float: components["schemas"]["FloatOutput"];
|
||||
zoe_depth_image_processor: components["schemas"]["ImageOutput"];
|
||||
face_off: components["schemas"]["FaceOffOutput"];
|
||||
tile_to_properties: components["schemas"]["TileToPropertiesOutput"];
|
||||
color_map_image_processor: components["schemas"]["ImageOutput"];
|
||||
lineart_anime_image_processor: components["schemas"]["ImageOutput"];
|
||||
face_identifier: components["schemas"]["ImageOutput"];
|
||||
float_math: components["schemas"]["FloatOutput"];
|
||||
mediapipe_face_processor: components["schemas"]["ImageOutput"];
|
||||
img_channel_multiply: components["schemas"]["ImageOutput"];
|
||||
metadata_item: components["schemas"]["MetadataItemOutput"];
|
||||
img_ilerp: components["schemas"]["ImageOutput"];
|
||||
conditioning: components["schemas"]["ConditioningOutput"];
|
||||
pidi_image_processor: components["schemas"]["ImageOutput"];
|
||||
seamless: components["schemas"]["SeamlessModeOutput"];
|
||||
latents: components["schemas"]["LatentsOutput"];
|
||||
img_chan: components["schemas"]["ImageOutput"];
|
||||
model_identifier: components["schemas"]["ModelIdentifierOutput"];
|
||||
noise: components["schemas"]["NoiseOutput"];
|
||||
string_join: components["schemas"]["StringOutput"];
|
||||
blank_image: components["schemas"]["ImageOutput"];
|
||||
calculate_image_tiles: components["schemas"]["CalculateImageTilesOutput"];
|
||||
invert_tensor_mask: components["schemas"]["MaskOutput"];
|
||||
save_image: components["schemas"]["ImageOutput"];
|
||||
unsharp_mask: components["schemas"]["ImageOutput"];
|
||||
image_mask_to_tensor: components["schemas"]["MaskOutput"];
|
||||
step_param_easing: components["schemas"]["FloatCollectionOutput"];
|
||||
merge_tiles_to_image: components["schemas"]["ImageOutput"];
|
||||
integer_collection: components["schemas"]["IntegerCollectionOutput"];
|
||||
calculate_image_tiles_even_split: components["schemas"]["CalculateImageTilesOutput"];
|
||||
integer_math: components["schemas"]["IntegerOutput"];
|
||||
range: components["schemas"]["IntegerCollectionOutput"];
|
||||
prompt_from_file: components["schemas"]["StringCollectionOutput"];
|
||||
segment_anything_processor: components["schemas"]["ImageOutput"];
|
||||
freeu: components["schemas"]["UNetOutput"];
|
||||
sub: components["schemas"]["IntegerOutput"];
|
||||
lresize: components["schemas"]["LatentsOutput"];
|
||||
float: components["schemas"]["FloatOutput"];
|
||||
float_collection: components["schemas"]["FloatCollectionOutput"];
|
||||
dynamic_prompt: components["schemas"]["StringCollectionOutput"];
|
||||
infill_lama: components["schemas"]["ImageOutput"];
|
||||
l2i: components["schemas"]["ImageOutput"];
|
||||
img_lerp: components["schemas"]["ImageOutput"];
|
||||
ip_adapter: components["schemas"]["IPAdapterOutput"];
|
||||
lora_collection_loader: components["schemas"]["LoRALoaderOutput"];
|
||||
color: components["schemas"]["ColorOutput"];
|
||||
tiled_multi_diffusion_denoise_latents: components["schemas"]["LatentsOutput"];
|
||||
cv_inpaint: components["schemas"]["ImageOutput"];
|
||||
lscale: components["schemas"]["LatentsOutput"];
|
||||
string: components["schemas"]["StringOutput"];
|
||||
sdxl_refiner_compel_prompt: components["schemas"]["ConditioningOutput"];
|
||||
string_join_three: components["schemas"]["StringOutput"];
|
||||
midas_depth_image_processor: components["schemas"]["ImageOutput"];
|
||||
esrgan: components["schemas"]["ImageOutput"];
|
||||
sdxl_refiner_model_loader: components["schemas"]["SDXLRefinerModelLoaderOutput"];
|
||||
mul: components["schemas"]["IntegerOutput"];
|
||||
normalbae_image_processor: components["schemas"]["ImageOutput"];
|
||||
infill_rgba: components["schemas"]["ImageOutput"];
|
||||
sdxl_model_loader: components["schemas"]["SDXLModelLoaderOutput"];
|
||||
vae_loader: components["schemas"]["VAEOutput"];
|
||||
float_to_int: components["schemas"]["IntegerOutput"];
|
||||
lora_selector: components["schemas"]["LoRASelectorOutput"];
|
||||
crop_latents: components["schemas"]["LatentsOutput"];
|
||||
img_mul: components["schemas"]["ImageOutput"];
|
||||
float_range: components["schemas"]["FloatCollectionOutput"];
|
||||
merge_metadata: components["schemas"]["MetadataOutput"];
|
||||
img_blur: components["schemas"]["ImageOutput"];
|
||||
boolean: components["schemas"]["BooleanOutput"];
|
||||
tile_image_processor: components["schemas"]["ImageOutput"];
|
||||
mlsd_image_processor: components["schemas"]["ImageOutput"];
|
||||
infill_patchmatch: components["schemas"]["ImageOutput"];
|
||||
img_pad_crop: components["schemas"]["ImageOutput"];
|
||||
leres_image_processor: components["schemas"]["ImageOutput"];
|
||||
sdxl_lora_loader: components["schemas"]["SDXLLoRALoaderOutput"];
|
||||
dw_openpose_image_processor: components["schemas"]["ImageOutput"];
|
||||
img_scale: components["schemas"]["ImageOutput"];
|
||||
pair_tile_image: components["schemas"]["PairTileImageOutput"];
|
||||
lblend: components["schemas"]["LatentsOutput"];
|
||||
range_of_size: components["schemas"]["IntegerCollectionOutput"];
|
||||
image_collection: components["schemas"]["ImageCollectionOutput"];
|
||||
calculate_image_tiles_min_overlap: components["schemas"]["CalculateImageTilesOutput"];
|
||||
img_channel_offset: components["schemas"]["ImageOutput"];
|
||||
alpha_mask_to_tensor: components["schemas"]["MaskOutput"];
|
||||
infill_cv2: components["schemas"]["ImageOutput"];
|
||||
mask_combine: components["schemas"]["ImageOutput"];
|
||||
string_split_neg: components["schemas"]["StringPosNegOutput"];
|
||||
sdxl_lora_collection_loader: components["schemas"]["SDXLLoRALoaderOutput"];
|
||||
lineart_image_processor: components["schemas"]["ImageOutput"];
|
||||
img_nsfw: components["schemas"]["ImageOutput"];
|
||||
image: components["schemas"]["ImageOutput"];
|
||||
content_shuffle_image_processor: components["schemas"]["ImageOutput"];
|
||||
canvas_paste_back: components["schemas"]["ImageOutput"];
|
||||
iterate: components["schemas"]["IterateInvocationOutput"];
|
||||
div: components["schemas"]["IntegerOutput"];
|
||||
latents_collection: components["schemas"]["LatentsCollectionOutput"];
|
||||
img_conv: components["schemas"]["ImageOutput"];
|
||||
mask_edge: components["schemas"]["ImageOutput"];
|
||||
conditioning_collection: components["schemas"]["ConditioningCollectionOutput"];
|
||||
img_hue_adjust: components["schemas"]["ImageOutput"];
|
||||
depth_anything_image_processor: components["schemas"]["ImageOutput"];
|
||||
lora_loader: components["schemas"]["LoRALoaderOutput"];
|
||||
sdxl_compel_prompt: components["schemas"]["ConditioningOutput"];
|
||||
add: components["schemas"]["IntegerOutput"];
|
||||
controlnet: components["schemas"]["ControlOutput"];
|
||||
color_correct: components["schemas"]["ImageOutput"];
|
||||
random_range: components["schemas"]["IntegerCollectionOutput"];
|
||||
denoise_latents: components["schemas"]["LatentsOutput"];
|
||||
metadata: components["schemas"]["MetadataOutput"];
|
||||
i2l: components["schemas"]["LatentsOutput"];
|
||||
show_image: components["schemas"]["ImageOutput"];
|
||||
img_paste: components["schemas"]["ImageOutput"];
|
||||
infill_tile: components["schemas"]["ImageOutput"];
|
||||
};
|
||||
/**
|
||||
* InvocationStartedEvent
|
||||
@@ -13218,19 +13206,6 @@ export type components = {
|
||||
*/
|
||||
type?: "url";
|
||||
};
|
||||
/** UncategorizedImageCounts */
|
||||
UncategorizedImageCounts: {
|
||||
/**
|
||||
* Image Count
|
||||
* @description The number of uncategorized images.
|
||||
*/
|
||||
image_count: number;
|
||||
/**
|
||||
* Asset Count
|
||||
* @description The number of uncategorized assets.
|
||||
*/
|
||||
asset_count: number;
|
||||
};
|
||||
/**
|
||||
* Unsharp Mask
|
||||
* @description Applies an unsharp mask filter to an image
|
||||
@@ -15188,20 +15163,6 @@ export type operations = {
|
||||
};
|
||||
};
|
||||
};
|
||||
/**
|
||||
* Get Uncategorized Image Counts
|
||||
* @description Gets count of images and assets for uncategorized images (images with no board assocation)
|
||||
*/
|
||||
get_uncategorized_image_counts: {
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
200: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["UncategorizedImageCounts"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
/**
|
||||
* Add Image To Board
|
||||
* @description Creates a board_image
|
||||
|
||||
@@ -36,6 +36,7 @@ export type AppDependencyVersions = S['AppDependencyVersions'];
|
||||
export type ImageDTO = S['ImageDTO'];
|
||||
export type BoardDTO = S['BoardDTO'];
|
||||
export type ImageCategory = S['ImageCategory'];
|
||||
export type OffsetPaginatedResults_ImageDTO_ = S['OffsetPaginatedResults_ImageDTO_'];
|
||||
|
||||
// Models
|
||||
export type ModelType = S['ModelType'];
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = "4.2.6"
|
||||
__version__ = "4.2.6post1"
|
||||
|
||||
@@ -127,16 +127,7 @@ def test_generate_id_with_board_id(monkeypatch: Any, mock_invoker: Invoker):
|
||||
|
||||
def mock_board_get(*args, **kwargs):
|
||||
return BoardRecord(
|
||||
board_id="12345",
|
||||
board_name="test_board_name",
|
||||
created_at="None",
|
||||
updated_at="None",
|
||||
archived=False,
|
||||
asset_count=0,
|
||||
image_count=0,
|
||||
cover_image_name="asdf.png",
|
||||
deleted_at=None,
|
||||
is_private=False,
|
||||
board_id="12345", board_name="test_board_name", created_at="None", updated_at="None", archived=False
|
||||
)
|
||||
|
||||
monkeypatch.setattr(mock_invoker.services.board_records, "get", mock_board_get)
|
||||
@@ -165,16 +156,7 @@ def test_handler_board_id(tmp_path: Path, monkeypatch: Any, mock_image_dto: Imag
|
||||
|
||||
def mock_board_get(*args, **kwargs):
|
||||
return BoardRecord(
|
||||
board_id="12345",
|
||||
board_name="test_board_name",
|
||||
created_at="None",
|
||||
updated_at="None",
|
||||
archived=False,
|
||||
asset_count=0,
|
||||
image_count=0,
|
||||
cover_image_name="asdf.png",
|
||||
deleted_at=None,
|
||||
is_private=False,
|
||||
board_id="12345", board_name="test_board_name", created_at="None", updated_at="None", archived=False
|
||||
)
|
||||
|
||||
monkeypatch.setattr(mock_invoker.services.board_records, "get", mock_board_get)
|
||||
|
||||
Reference in New Issue
Block a user