Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
4407650
Added backend and frontend support for a modal image search dialog
JPPhoto Feb 14, 2026
e3ebb6e
Fixed 'All Boards' selection
JPPhoto Feb 15, 2026
e6dc69f
Merge branch 'invoke-ai:main' into search-modal
JPPhoto Feb 16, 2026
50990e1
Merge branch 'invoke-ai:main' into search-modal
JPPhoto Feb 21, 2026
0e1eba2
Merge branch 'invoke-ai:main' into search-modal
JPPhoto Feb 23, 2026
350326f
Merge branch 'invoke-ai:main' into search-modal
JPPhoto Feb 24, 2026
f5bb034
Updated schema / ruff
JPPhoto Feb 24, 2026
91b45a9
ruff format
JPPhoto Feb 24, 2026
4084472
Merge branch 'invoke-ai:main' into search-modal
JPPhoto Feb 25, 2026
ca7d671
Merge branch 'main' into search-modal
JPPhoto Feb 26, 2026
45dc4c6
Merge branch 'main' into search-modal
JPPhoto Feb 27, 2026
7672d77
Merge branch 'main' into search-modal
JPPhoto Feb 27, 2026
f404702
Fixed indentation
JPPhoto Feb 27, 2026
e99a133
Merge branch 'main' into search-modal
JPPhoto Feb 28, 2026
bed7a82
Merge branch 'main' into search-modal
JPPhoto Mar 2, 2026
30b8e73
Merge branch 'main' into search-modal
JPPhoto Mar 9, 2026
ef66566
Merge branch 'main' into search-modal
JPPhoto Mar 10, 2026
7c7b06e
Merge branch 'invoke-ai:main' into search-modal
JPPhoto Mar 11, 2026
fca755e
Added tests for search filters
JPPhoto Mar 11, 2026
38e64f5
Merge branch 'invoke-ai:main' into search-modal
JPPhoto Mar 11, 2026
fcfc94a
Merge branch 'main' into search-modal
JPPhoto Mar 20, 2026
686fed1
Merge branch 'invoke-ai:main' into search-modal
JPPhoto Mar 24, 2026
83c8784
Merge branch 'invoke-ai:main' into search-modal
JPPhoto Mar 24, 2026
00e618b
Merge branch 'invoke-ai:main' into search-modal
JPPhoto Mar 24, 2026
339fc06
Merge branch 'invoke-ai:main' into search-modal
JPPhoto Mar 26, 2026
10f16c6
Merge branch 'invoke-ai:main' into search-modal
JPPhoto Mar 29, 2026
05b8a0e
Merge branch 'invoke-ai:main' into search-modal
JPPhoto Apr 5, 2026
30959f2
Merge branch 'invoke-ai:main' into search-modal
JPPhoto Apr 5, 2026
4171927
Merge branch 'invoke-ai:main' into search-modal
JPPhoto Apr 5, 2026
8f2348e
Merge remote-tracking branch 'origin/main' into codex-tmp/update-bran…
JPPhoto Apr 6, 2026
67c4013
Merge remote-tracking branch 'origin/main' into codex-tmp/update-bran…
JPPhoto Apr 6, 2026
ca9a46b
Merge remote-tracking branch 'origin/main' into codex-tmp/update-bran…
JPPhoto Apr 7, 2026
7733ae4
Merge remote-tracking branch 'origin/main' into codex-tmp/update-bran…
JPPhoto Apr 7, 2026
0f2f726
Merge remote-tracking branch 'origin/main' into codex-tmp/update-bran…
JPPhoto Apr 9, 2026
d2588b5
Merge remote-tracking branch 'origin/main' into codex-tmp/update-bran…
JPPhoto Apr 9, 2026
728f3d0
Merge remote-tracking branch 'origin/main' into codex-tmp/update-bran…
JPPhoto Apr 9, 2026
de935b6
Merge remote-tracking branch 'origin/main' into codex-tmp/update-bran…
JPPhoto Apr 10, 2026
1be5eef
Merge remote-tracking branch 'origin/main' into codex-tmp/update-bran…
JPPhoto Apr 10, 2026
4dc8274
Merge remote-tracking branch 'origin/main' into codex-tmp/update-bran…
JPPhoto Apr 10, 2026
f393f25
Merge remote-tracking branch 'origin/main' into codex-tmp/update-bran…
JPPhoto Apr 12, 2026
f1639a7
Merge remote-tracking branch 'origin/main' into search-modal
JPPhoto Apr 13, 2026
e6fdc79
chore: ruff
JPPhoto Apr 14, 2026
c8b023a
Merge remote-tracking branch 'origin/main' into codex-tmp/update-bran…
JPPhoto Apr 14, 2026
20ddcd0
Merge branch 'main' into search-modal
JPPhoto Apr 14, 2026
4eb1e8c
Merge branch 'main' into search-modal
JPPhoto Apr 14, 2026
99bca36
Merge remote-tracking branch 'origin/main' into codex-tmp/update-bran…
JPPhoto Apr 14, 2026
8ced41c
Merge remote-tracking branch 'origin/main' into codex-tmp/update-bran…
JPPhoto Apr 14, 2026
b6ff509
Merge remote-tracking branch 'origin/main' into codex-tmp/update-bran…
JPPhoto Apr 17, 2026
5c7880a
Merge remote-tracking branch 'origin/main' into codex-tmp/update-bran…
JPPhoto Apr 17, 2026
195093a
Merge remote-tracking branch 'origin/main' into codex-tmp/update-bran…
JPPhoto Apr 18, 2026
345da94
Merge remote-tracking branch 'origin/main' into codex-tmp/update-bran…
JPPhoto Apr 18, 2026
ba2ad29
Merge branch 'main' into search-modal
JPPhoto Apr 19, 2026
02045a2
Merge remote-tracking branch 'origin/main' into codex-tmp/update-bran…
JPPhoto Apr 19, 2026
114c425
Merge remote-tracking branch 'origin/main' into codex-tmp/update-bran…
JPPhoto Apr 19, 2026
a0d0f71
Merge remote-tracking branch 'origin/main' into codex-tmp/update-bran…
JPPhoto Apr 20, 2026
43cd7c4
Merge remote-tracking branch 'origin/main' into codex-tmp/update-bran…
JPPhoto Apr 20, 2026
6955e92
Merge remote-tracking branch 'origin/main' into codex-tmp/update-bran…
JPPhoto Apr 20, 2026
2c37f39
Merge remote-tracking branch 'origin/main' into codex-tmp/update-bran…
JPPhoto Apr 20, 2026
76cf6bf
Merge remote-tracking branch 'origin/main' into codex-tmp/update-bran…
JPPhoto Apr 20, 2026
e6932b0
Merge remote-tracking branch 'origin/main' into codex-tmp/update-bran…
JPPhoto Apr 21, 2026
df3e838
Merge remote-tracking branch 'origin/main' into codex-tmp/update-bran…
JPPhoto Apr 21, 2026
2f4c54b
Merge remote-tracking branch 'origin/main' into codex-tmp/update-bran…
JPPhoto Apr 21, 2026
c97ac12
Merge remote-tracking branch 'origin/main' into codex-tmp/update-bran…
JPPhoto Apr 22, 2026
97a9268
Merge remote-tracking branch 'origin/main' into codex-tmp/update-bran…
JPPhoto Apr 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 118 additions & 1 deletion invokeai/app/api/routers/images.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import io
import json
import traceback
from typing import ClassVar, Optional
from typing import ClassVar, Literal, Optional

from fastapi import BackgroundTasks, Body, HTTPException, Path, Query, Request, Response, UploadFile
from fastapi.responses import FileResponse
Expand Down Expand Up @@ -38,6 +38,19 @@
IMAGE_MAX_AGE = 31536000


class ImageSearchBody(BaseModel):
file_name_term: Optional[str] = Field(default=None)
metadata_term: Optional[str] = Field(default=None)
width_min: Optional[int] = Field(default=None)
width_max: Optional[int] = Field(default=None)
width_exact: Optional[int] = Field(default=None)
height_min: Optional[int] = Field(default=None)
height_max: Optional[int] = Field(default=None)
height_exact: Optional[int] = Field(default=None)
board_ids: Optional[list[str]] = Field(default=None)
starred_mode: Literal["include", "exclude", "only"] = Field(default="include")


def _assert_image_owner(image_name: str, current_user: CurrentUserOrDefault) -> None:
"""Raise 403 if the current user does not own the image and is not an admin.

Expand Down Expand Up @@ -521,6 +534,18 @@ async def list_image_dtos(
order_dir: SQLiteDirection = Query(default=SQLiteDirection.Descending, description="The order of sort"),
starred_first: bool = Query(default=True, description="Whether to sort by starred images first"),
search_term: Optional[str] = Query(default=None, description="The term to search for"),
file_name_term: Optional[str] = Query(default=None, description="File name search term"),
metadata_term: Optional[str] = Query(default=None, description="Metadata search term"),
width_min: Optional[int] = Query(default=None, description="Minimum image width"),
width_max: Optional[int] = Query(default=None, description="Maximum image width"),
width_exact: Optional[int] = Query(default=None, description="Exact image width"),
height_min: Optional[int] = Query(default=None, description="Minimum image height"),
height_max: Optional[int] = Query(default=None, description="Maximum image height"),
height_exact: Optional[int] = Query(default=None, description="Exact image height"),
board_ids: Optional[list[str]] = Query(default=None, description="Boards to include, supports 'none'"),
starred_mode: Literal["include", "exclude", "only"] = Query(
default="include", description="How to handle starred images"
),
) -> OffsetPaginatedResults[ImageDTO]:
"""Gets a list of image DTOs for the current user"""

Expand All @@ -539,6 +564,16 @@ async def list_image_dtos(
is_intermediate,
board_id,
search_term,
file_name_term,
metadata_term,
width_min,
width_max,
width_exact,
height_min,
height_max,
height_exact,
board_ids,
starred_mode,
current_user.user_id,
)

Expand Down Expand Up @@ -776,6 +811,18 @@ async def get_image_names(
order_dir: SQLiteDirection = Query(default=SQLiteDirection.Descending, description="The order of sort"),
starred_first: bool = Query(default=True, description="Whether to sort by starred images first"),
search_term: Optional[str] = Query(default=None, description="The term to search for"),
file_name_term: Optional[str] = Query(default=None, description="File name search term"),
metadata_term: Optional[str] = Query(default=None, description="Metadata search term"),
width_min: Optional[int] = Query(default=None, description="Minimum image width"),
width_max: Optional[int] = Query(default=None, description="Maximum image width"),
width_exact: Optional[int] = Query(default=None, description="Exact image width"),
height_min: Optional[int] = Query(default=None, description="Minimum image height"),
height_max: Optional[int] = Query(default=None, description="Maximum image height"),
height_exact: Optional[int] = Query(default=None, description="Exact image height"),
board_ids: Optional[list[str]] = Query(default=None, description="Boards to include, supports 'none'"),
starred_mode: Literal["include", "exclude", "only"] = Query(
default="include", description="How to handle starred images"
),
) -> ImageNamesResult:
"""Gets ordered list of image names with metadata for optimistic updates"""

Expand All @@ -792,6 +839,16 @@ async def get_image_names(
is_intermediate=is_intermediate,
board_id=board_id,
search_term=search_term,
file_name_term=file_name_term,
metadata_term=metadata_term,
width_min=width_min,
width_max=width_max,
width_exact=width_exact,
height_min=height_min,
height_max=height_max,
height_exact=height_exact,
board_ids=board_ids,
starred_mode=starred_mode,
user_id=current_user.user_id,
is_admin=current_user.is_admin,
)
Expand All @@ -800,6 +857,66 @@ async def get_image_names(
raise HTTPException(status_code=500, detail="Failed to get image names")


@images_router.post("/search", operation_id="search_images", response_model=OffsetPaginatedResults[ImageDTO])
async def search_images(
body: ImageSearchBody,
image_origin: Optional[ResourceOrigin] = Query(default=None),
categories: Optional[list[ImageCategory]] = Query(default=None),
is_intermediate: Optional[bool] = Query(default=None),
offset: int = Query(default=0),
limit: int = Query(default=100),
order_dir: SQLiteDirection = Query(default=SQLiteDirection.Descending),
starred_first: bool = Query(default=True),
) -> OffsetPaginatedResults[ImageDTO]:
return ApiDependencies.invoker.services.images.get_many(
offset=offset,
limit=limit,
starred_first=starred_first,
order_dir=order_dir,
image_origin=image_origin,
categories=categories,
is_intermediate=is_intermediate,
file_name_term=body.file_name_term,
metadata_term=body.metadata_term,
width_min=body.width_min,
width_max=body.width_max,
width_exact=body.width_exact,
height_min=body.height_min,
height_max=body.height_max,
height_exact=body.height_exact,
board_ids=body.board_ids,
starred_mode=body.starred_mode,
)


@images_router.post("/search/names", operation_id="search_image_names", response_model=ImageNamesResult)
async def search_image_names(
body: ImageSearchBody,
image_origin: Optional[ResourceOrigin] = Query(default=None),
categories: Optional[list[ImageCategory]] = Query(default=None),
is_intermediate: Optional[bool] = Query(default=None),
order_dir: SQLiteDirection = Query(default=SQLiteDirection.Descending),
starred_first: bool = Query(default=True),
) -> ImageNamesResult:
return ApiDependencies.invoker.services.images.get_image_names(
starred_first=starred_first,
order_dir=order_dir,
image_origin=image_origin,
categories=categories,
is_intermediate=is_intermediate,
file_name_term=body.file_name_term,
metadata_term=body.metadata_term,
width_min=body.width_min,
width_max=body.width_max,
width_exact=body.width_exact,
height_min=body.height_min,
height_max=body.height_max,
height_exact=body.height_exact,
board_ids=body.board_ids,
starred_mode=body.starred_mode,
)


@images_router.post(
"/images_by_names",
operation_id="get_images_by_names",
Expand Down
20 changes: 20 additions & 0 deletions invokeai/app/services/image_records/image_records_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,16 @@ def get_many(
is_intermediate: Optional[bool] = None,
board_id: Optional[str] = None,
search_term: Optional[str] = None,
file_name_term: Optional[str] = None,
metadata_term: Optional[str] = None,
width_min: Optional[int] = None,
width_max: Optional[int] = None,
width_exact: Optional[int] = None,
height_min: Optional[int] = None,
height_max: Optional[int] = None,
height_exact: Optional[int] = None,
board_ids: Optional[list[str]] = None,
starred_mode: Optional[str] = None,
user_id: Optional[str] = None,
is_admin: bool = False,
) -> OffsetPaginatedResults[ImageRecord]:
Expand Down Expand Up @@ -117,6 +127,16 @@ def get_image_names(
is_intermediate: Optional[bool] = None,
board_id: Optional[str] = None,
search_term: Optional[str] = None,
file_name_term: Optional[str] = None,
metadata_term: Optional[str] = None,
width_min: Optional[int] = None,
width_max: Optional[int] = None,
width_exact: Optional[int] = None,
height_min: Optional[int] = None,
height_max: Optional[int] = None,
height_exact: Optional[int] = None,
board_ids: Optional[list[str]] = None,
starred_mode: Optional[str] = None,
user_id: Optional[str] = None,
is_admin: bool = False,
) -> ImageNamesResult:
Expand Down
Loading
Loading