mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2026-07-08 23:47:24 +08:00
The old file_path response field was a namespaced storage locator (models/checkpoints/foo.safetensors): not an absolute path, not unique identity, and not the value a loader consumes. Nothing needs that shape on the wire (hash/ID-based locating is the long-term direction), so it is dropped rather than renamed; the storage-root matching stays internal, powering display_name. What loaders DO need is the in-root loader path (category dropped: models/checkpoints/foo/bar.safetensors -> foo/bar.safetensors). Serve it as a first-class loader_path field, persisted on asset_references (migration 0006) and written by every ingest pipeline at insert, so responses read the column verbatim. Like the model_type tags, loader_path is a seed-time derivative of the model folder registry, maintained by the same scan lifecycle (new files seed fresh values, pruning retires rows whose bucket disappeared). Rows predating the column serve a null loader_path; databases from before this stack already need recreating for the base branch's tag changes. loader_path resolves every registered base including extra_model_paths entries; display_name only the canonical storage roots. A file can therefore be loadable with no display name (extra-path models) or the reverse (unregistered files under the models root), and loader_path is null exactly when no loader can resolve the file.
116 lines
2.4 KiB
Python
116 lines
2.4 KiB
Python
from dataclasses import dataclass
|
|
from datetime import datetime
|
|
from typing import Any, NamedTuple
|
|
|
|
from app.assets.database.models import Asset, AssetReference
|
|
|
|
UserMetadata = dict[str, Any] | None
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class AssetData:
|
|
hash: str | None
|
|
size_bytes: int | None
|
|
mime_type: str | None
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class ReferenceData:
|
|
"""Data transfer object for AssetReference."""
|
|
|
|
id: str
|
|
name: str
|
|
file_path: str | None
|
|
user_metadata: UserMetadata
|
|
preview_id: str | None
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
loader_path: str | None = None
|
|
system_metadata: dict[str, Any] | None = None
|
|
job_id: str | None = None
|
|
last_access_time: datetime | None = None
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class AssetDetailResult:
|
|
ref: ReferenceData
|
|
asset: AssetData | None
|
|
tags: list[str]
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class RegisterAssetResult:
|
|
ref: ReferenceData
|
|
asset: AssetData
|
|
tags: list[str]
|
|
created: bool
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class IngestResult:
|
|
asset_created: bool
|
|
asset_updated: bool
|
|
ref_created: bool
|
|
ref_updated: bool
|
|
reference_id: str | None
|
|
|
|
|
|
class TagUsage(NamedTuple):
|
|
name: str
|
|
count: int
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class AssetSummaryData:
|
|
ref: ReferenceData
|
|
asset: AssetData | None
|
|
tags: list[str]
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class ListAssetsResult:
|
|
items: list[AssetSummaryData]
|
|
total: int
|
|
next_cursor: str | None = None
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class DownloadResolutionResult:
|
|
abs_path: str
|
|
content_type: str
|
|
download_name: str
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class UploadResult:
|
|
ref: ReferenceData
|
|
asset: AssetData
|
|
tags: list[str]
|
|
created_new: bool
|
|
|
|
|
|
def extract_reference_data(ref: AssetReference) -> ReferenceData:
|
|
return ReferenceData(
|
|
id=ref.id,
|
|
name=ref.name,
|
|
file_path=ref.file_path,
|
|
loader_path=ref.loader_path,
|
|
user_metadata=ref.user_metadata,
|
|
preview_id=ref.preview_id,
|
|
system_metadata=ref.system_metadata,
|
|
job_id=ref.job_id,
|
|
created_at=ref.created_at,
|
|
updated_at=ref.updated_at,
|
|
last_access_time=ref.last_access_time,
|
|
)
|
|
|
|
|
|
def extract_asset_data(asset: Asset | None) -> AssetData | None:
|
|
if asset is None:
|
|
return None
|
|
return AssetData(
|
|
hash=asset.hash,
|
|
size_bytes=asset.size_bytes,
|
|
mime_type=asset.mime_type,
|
|
)
|