[pull] master from Comfy-Org:master by pull[bot] · Pull Request #1036 · code/app-python-comfyui · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 98 additions & 5 deletions app/assets/api/routes.py
26 changes: 20 additions & 6 deletions app/assets/api/schemas_in.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,12 @@ class ParsedUpload:


class ListAssetsQuery(BaseModel):
include_tags: list[str] = Field(default_factory=list)
exclude_tags: list[str] = Field(default_factory=list)
# Deprecated spellings: include_tags ≡ tags_all, exclude_tags ≡ tags_none.
include_tags: list[str] = Field(default_factory=list, deprecated=True)
exclude_tags: list[str] = Field(default_factory=list, deprecated=True)
tags_all: list[str] = Field(default_factory=list)
tags_any: list[str] = Field(default_factory=list)
tags_none: list[str] = Field(default_factory=list)
name_contains: str | None = None

# Accept either a JSON string (query param) or a dict
Expand All @@ -70,7 +74,10 @@ class ListAssetsQuery(BaseModel):
)
order: Literal["asc", "desc"] = "desc"

@field_validator("include_tags", "exclude_tags", mode="before")
@field_validator(
"include_tags", "exclude_tags", "tags_all", "tags_any", "tags_none",
mode="before",
)
@classmethod
def _split_csv_tags(cls, v):
# Accept "a,b,c" or ["a","b"] (we are liberal in what we accept)
Expand Down Expand Up @@ -154,13 +161,20 @@ def _normalize_tags_field(cls, v):


class TagsRefineQuery(BaseModel):
include_tags: list[str] = Field(default_factory=list)
exclude_tags: list[str] = Field(default_factory=list)
# Deprecated spellings: include_tags ≡ tags_all, exclude_tags ≡ tags_none.
include_tags: list[str] = Field(default_factory=list, deprecated=True)
exclude_tags: list[str] = Field(default_factory=list, deprecated=True)
tags_all: list[str] = Field(default_factory=list)
tags_any: list[str] = Field(default_factory=list)
tags_none: list[str] = Field(default_factory=list)
name_contains: str | None = None
metadata_filter: dict[str, Any] | None = None
limit: conint(ge=1, le=1000) = 100

@field_validator("include_tags", "exclude_tags", mode="before")
@field_validator(
"include_tags", "exclude_tags", "tags_all", "tags_any", "tags_none",
mode="before",
)
@classmethod
def _split_csv_tags(cls, v):
if v is None:
Expand Down
6 changes: 4 additions & 2 deletions app/assets/database/queries/asset_reference.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,8 @@ def list_references_page(
order: str | None = None,
after_cursor_value: object | None = None,
after_cursor_id: str | None = None,
# Appended last so pre-existing positional callers keep binding correctly.
any_tags: Sequence[str] | None = None,
) -> tuple[list[AssetReference], dict[str, list[str]], int]:
"""List references with pagination, filtering, and sorting.

Expand All @@ -293,7 +295,7 @@ def list_references_page(
escaped, esc = escape_sql_like_string(name_contains)
base = base.where(AssetReference.name.ilike(f"%{escaped}%", escape=esc))

base = apply_tag_filters(base, include_tags, exclude_tags)
base = apply_tag_filters(base, include_tags, exclude_tags, any_tags)
base = apply_metadata_filter(base, metadata_filter)

sort = (sort or "created_at").lower()
Expand Down Expand Up @@ -345,7 +347,7 @@ def list_references_page(
count_stmt = count_stmt.where(
AssetReference.name.ilike(f"%{escaped}%", escape=esc)
)
count_stmt = apply_tag_filters(count_stmt, include_tags, exclude_tags)
count_stmt = apply_tag_filters(count_stmt, include_tags, exclude_tags, any_tags)
count_stmt = apply_metadata_filter(count_stmt, metadata_filter)

total = int(session.execute(count_stmt).scalar_one() or 0)
Expand Down
13 changes: 12 additions & 1 deletion app/assets/database/queries/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,13 @@ def apply_tag_filters(
stmt: sa.sql.Select,
include_tags: Sequence[str] | None = None,
exclude_tags: Sequence[str] | None = None,
any_tags: Sequence[str] | None = None,
) -> sa.sql.Select:
"""include_tags: every tag must be present; exclude_tags: none may be present."""
"""include_tags: every tag must be present; any_tags: at least one must be
present; exclude_tags: none may be present."""
include_tags = normalize_tags(include_tags)
exclude_tags = normalize_tags(exclude_tags)
any_tags = normalize_tags(any_tags)

if include_tags:
for tag_name in include_tags:
Expand All @@ -74,6 +77,14 @@ def apply_tag_filters(
)
)

if any_tags:
stmt = stmt.where(
exists().where(
(AssetReferenceTag.asset_reference_id == AssetReference.id)
& (AssetReferenceTag.tag_name.in_(any_tags))
)
)

if exclude_tags:
stmt = stmt.where(
~exists().where(
Expand Down
4 changes: 3 additions & 1 deletion app/assets/database/queries/tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,8 @@ def list_tag_counts_for_filtered_assets(
name_contains: str | None = None,
metadata_filter: dict | None = None,
limit: int = 100,
# Appended last so pre-existing positional callers keep binding correctly.
any_tags: Sequence[str] | None = None,
) -> dict[str, int]:
"""Return tag counts for assets matching the given filters.

Expand All @@ -359,7 +361,7 @@ def list_tag_counts_for_filtered_assets(
escaped, esc = escape_sql_like_string(name_contains)
ref_sq = ref_sq.where(AssetReference.name.ilike(f"%{escaped}%", escape=esc))

ref_sq = apply_tag_filters(ref_sq, include_tags, exclude_tags)
ref_sq = apply_tag_filters(ref_sq, include_tags, exclude_tags, any_tags)
ref_sq = apply_metadata_filter(ref_sq, metadata_filter)
ref_sq = ref_sq.subquery()

Expand Down
3 changes: 3 additions & 0 deletions app/assets/services/asset_management.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,8 @@ def list_assets_page(
sort: str = "created_at",
order: str = "desc",
after: str | None = None,
# Appended last so pre-existing positional callers keep binding correctly.
any_tags: Sequence[str] | None = None,
) -> ListAssetsResult:
"""List assets with optional cursor pagination.

Expand Down Expand Up @@ -317,6 +319,7 @@ def list_assets_page(
owner_id=owner_id,
include_tags=include_tags,
exclude_tags=exclude_tags,
any_tags=any_tags,
name_contains=name_contains,
metadata_filter=metadata_filter,
limit=fetch_limit,
Expand Down
3 changes: 3 additions & 0 deletions app/assets/services/tagging.py
Loading
Loading