fix(assets): restore display_name and file_path as served fields; unify error envelopes (review-5, review2-8)

This commit is contained in:
Simon Pinfold
2026-08-26 09:29:50 -07:00
parent ef92de23af
commit bb4dd0eb47
6 changed files with 77 additions and 17 deletions

View File

@@ -306,9 +306,13 @@ def _build_asset_response(
else:
preview_url = None
if result.ref.file_path:
paths = compute_asset_response_paths(result.ref.file_path)
logical_path = paths[0] if paths else None
display_name = paths[1] if paths else None
# In-root loader path (model category dropped): what model loaders consume.
loader_path = result.ref.loader_path
else:
logical_path, display_name = None, None
loader_path = None
asset_content_hash = result.asset.hash if result.asset else None
return schemas_out.Asset(
@@ -316,6 +320,8 @@ def _build_asset_response(
name=result.ref.name,
hash=asset_content_hash,
loader_path=loader_path,
display_name=display_name,
file_path=logical_path,
size=int(result.asset.size_bytes) if result.asset else None,
mime_type=result.asset.mime_type if result.asset else None,
tags=result.tags,
@@ -337,6 +343,9 @@ def _build_record_response(
preview_paths: dict[str, str],
) -> schemas_out.Asset:
content = record.content
paths = compute_asset_response_paths(content.path)
logical_path = paths[0] if paths else None
display_name = paths[1] if paths else None
if record.preview_id:
preview_url = _build_view_url(preview_paths.get(record.preview_id))
else:
@@ -353,6 +362,8 @@ def _build_record_response(
name=record.name,
hash=content.hash,
loader_path=record.loader_path,
display_name=display_name,
file_path=logical_path,
size=content.size_bytes,
mime_type=record.mime_type,
tags=tags,
@@ -452,12 +463,8 @@ async def list_assets_route(request: web.Request) -> web.Response:
GET request to list assets.
"""
if "metadata_filter" in request.query:
return web.json_response(
{
"error": "UNSUPPORTED_PARAM",
"message": "metadata_filter is no longer supported",
},
status=400,
return _build_error_response(
400, "UNSUPPORTED_PARAM", "metadata_filter is no longer supported"
)
query_dict = get_query_dict(request)
@@ -977,12 +984,8 @@ async def delete_asset_tags(request: web.Request) -> web.Response:
async def get_tags_refine(request: web.Request) -> web.Response:
"""GET request to get tag histogram for filtered assets."""
if "metadata_filter" in request.query:
return web.json_response(
{
"error": "UNSUPPORTED_PARAM",
"message": "metadata_filter is no longer supported",
},
status=400,
return _build_error_response(
400, "UNSUPPORTED_PARAM", "metadata_filter is no longer supported"
)
query_dict = get_query_dict(request)

View File

@@ -15,7 +15,14 @@ class Asset(BaseModel):
default=None,
description="The value a loader consumes to load this asset. `None` when no loader can resolve the file.",
)
display_name: str | None = Field(default=None, exclude=True)
display_name: str | None = Field(
default=None,
description="Human-facing label for the asset. Not unique.",
)
file_path: str | None = Field(
default=None,
description='Relative path in global-namespace-root form (e.g. "models/checkpoints/flux.safetensors").',
)
size: int | None = None
mime_type: str | None = None
tags: list[str] = Field(default_factory=list)

View File

@@ -7,6 +7,10 @@ components:
description: Timestamp when the asset was created
format: date-time
type: string
display_name:
description: Human-facing label for the asset. Not unique.
nullable: true
type: string
file_path:
description: Relative path in global-namespace-root form (e.g. "models/checkpoints/flux.safetensors")
nullable: true
@@ -141,6 +145,10 @@ components:
AssetUpdated:
description: Response returned when an existing asset is successfully updated.
properties:
display_name:
description: Human-facing label for the asset. Not unique.
nullable: true
type: string
file_path:
description: Relative path in global-namespace-root form (e.g. "models/checkpoints/flux.safetensors")
nullable: true

View File

@@ -70,6 +70,8 @@ def test_null_stored_loader_path_is_served_as_null(tmp_path: Path):
resp = _build_asset_response(result, {})
assert resp.loader_path is None
assert resp.display_name == "checkpoints/bar.safetensors"
assert resp.file_path == "models/checkpoints/bar.safetensors"
def test_all_path_fields_null_without_file_path():
@@ -79,3 +81,37 @@ def test_all_path_fields_null_without_file_path():
resp = _build_asset_response(result, {})
assert resp.loader_path is None
assert resp.display_name is None
assert resp.file_path is None
def test_display_name_and_file_path_are_serialized(tmp_path: Path):
"""Serialisation guard: both display_name and file_path survive
model_dump(mode="json").
display_name previously carried exclude=True, which silently dropped it
from every serialised response; file_path was never wired through at all.
Asserting on the dumped payload (not just attribute access) is what would
have caught either regression, so this guards the JSON contract directly.
"""
models = tmp_path / "models"
ckpt = models / "checkpoints"
ckpt.mkdir(parents=True)
f = ckpt / "flux.safetensors"
f.touch()
with patch("app.assets.services.path_utils.folder_paths") as mock_fp, patch(
"app.assets.services.path_utils.get_comfy_models_folders",
return_value=[("checkpoints", [str(ckpt)], {".safetensors"})],
):
mock_fp.get_input_directory.return_value = str(tmp_path / "in")
mock_fp.get_output_directory.return_value = str(tmp_path / "out")
mock_fp.get_temp_directory.return_value = str(tmp_path / "tmp")
mock_fp.models_dir = str(models)
result = _make_result(file_path=str(f), loader_path=None)
resp = _build_asset_response(result, {})
dumped = resp.model_dump(mode="json")
assert dumped["display_name"] == "checkpoints/flux.safetensors"
assert dumped["file_path"] == "models/checkpoints/flux.safetensors"

View File

@@ -43,8 +43,11 @@ def test_record_list_rejects_metadata_filter(http, api_base):
assert response.status_code == 400
assert response.json() == {
"error": "UNSUPPORTED_PARAM",
"message": "metadata_filter is no longer supported",
"error": {
"code": "UNSUPPORTED_PARAM",
"message": "metadata_filter is no longer supported",
"details": {},
}
}

View File

@@ -15,6 +15,9 @@ def test_tag_refine_rejects_metadata_filter(http, api_base):
assert response.status_code == 400
assert response.json() == {
"error": "UNSUPPORTED_PARAM",
"message": "metadata_filter is no longer supported",
"error": {
"code": "UNSUPPORTED_PARAM",
"message": "metadata_filter is no longer supported",
"details": {},
}
}