Skip to content

client.assets

Bases: SyncResource

Sync image-assets namespace (client.assets).

upload_init

upload_init(dataset_id: UUID | str, *, filename: str, size_bytes: int, content_type: str) -> AssetUploadSinglePart | AssetUploadMultipart

Reserve one presigned upload. Returns a single-part PUT for normal images; a multipart plan for files over the 100 MB threshold (then finish with :meth:upload_complete). PUT the bytes with exactly the content_type you declare here — the presigned signature pins it.

Parameters:

Name Type Description Default
content_type str

MIME; must be in the accepted image set (ADR-0017).

required

Raises:

Type Description
PayloadTooLargeError

over the 50 MB per-image cap (ASSET_TOO_LARGE).

BadRequestError

unaccepted MIME (ASSET_FORMAT_UNSUPPORTED).

ConflictError

dataset at its 1M-asset cap (DATASET_ASSET_CAP_EXCEEDED) or tenant asset cap (RESOURCE_LIMIT_EXCEEDED).

upload_init_batch

upload_init_batch(dataset_id: UUID | str, *, items: list[UploadItemParam]) -> AssetUploadBatchResult

Reserve up to 100 single-part uploads in one call (folder ingest).

Parameters:

Name Type Description Default
items list[UploadItemParam]

up to 100 {"client_ref", "filename", "size_bytes", "content_type"} dicts. client_ref is echoed back so you can map each result to its source.

required

Per-file failures are returned as upload_type="error" items (not raised); files over the single-part threshold come back with error_code="USE_PER_FILE_INIT".

Raises:

Type Description
ConflictError

tenant asset cap reached (RESOURCE_LIMIT_EXCEEDED).

UnprocessableError

empty batch or > 100 items.

upload_complete

upload_complete(dataset_id: UUID | str, upload_id: str, *, s3_key: str, parts: list[SearchQueryPartParam]) -> None

Finalize a multipart upload after every part is PUT. Returns nothing (204); the asset row is then created asynchronously by the S3-event worker.

Parameters:

Name Type Description Default
upload_id str

the id from the multipart :meth:upload_init response.

required
s3_key str

echoed from that same response.

required
parts list[SearchQueryPartParam]

[{"part_number", "etag"}] — the ETag S3 returned for each PUT.

required

Raises:

Type Description
BadRequestError

s3_key not owned by this tenant, or the part list doesn't match S3's record of the upload.

list

list(dataset_id: UUID | str, *, q: str | None = None, uploaded_by: UUID | str | None = None, status: AssetStatus | str | None = None, content_type: str | None = None, embedding_status: EmbeddingStatus | str | None = None, created_at_from: datetime | str | None = None, created_at_to: datetime | str | None = None, include: str | None = None, include_total: bool = False, limit: int | None = None, cursor: str | None = None) -> Page[Asset]

List a dataset's assets, newest first (cursor-paginated; iterate for all).

Parameters:

Name Type Description Default
q str | None

case-insensitive substring on the filename.

None
uploaded_by UUID | str | None

filter to assets uploaded by this user id. Exact match.

None
status AssetStatus | str | None

processing state filter (pending / processing / ready / failed / deleted). Exact match.

None
content_type str | None

filter to a single MIME type (e.g. image/jpeg). Exact match.

None
embedding_status EmbeddingStatus | str | None

pending / ready — use pending to find assets the next embed pass will pick up.

None
created_at_from datetime | str | None

keep only assets created at or after this instant (inclusive lower bound; datetime or ISO-8601 string).

None
created_at_to datetime | str | None

keep only assets created at or before this instant (inclusive upper bound; datetime or ISO-8601 string).

None
include str | None

CSV of embeds — projects and/or thumbnail_url.

None
include_total bool

also populate page.total (a COUNT; ~150ms at cap).

False

Raises:

Type Description
BadRequestError

malformed cursor (INVALID_CURSOR).

UnprocessableError

invalid enum filter value.

get

get(dataset_id: UUID | str, asset_id: UUID | str) -> Asset

Fetch one asset.

Raises:

Type Description
NotFoundError

no such asset in this dataset (ASSET_NOT_FOUND).

download_url

download_url(dataset_id: UUID | str, asset_id: UUID | str) -> AssetDownloadUrl

Presigned GET URL for the asset's full-resolution object (1-hour expiry).

Raises:

Type Description
NotFoundError

no such asset in this dataset (ASSET_NOT_FOUND).

thumbnail_url

thumbnail_url(dataset_id: UUID | str, asset_id: UUID | str) -> AssetDownloadUrl

Presigned GET URL for the 320px thumbnail (ADR-0026).

Raises:

Type Description
NotFoundError

no such asset (ASSET_NOT_FOUND), or the thumbnail was never generated — fall back to :meth:download_url on 404.

search

search(dataset_id: UUID | str, *, parts: list[SearchQueryPartParam], instruction: str | None = None, filters: AssetFilterParam | None = None, k: int = 50, include_total: bool = False) -> SearchResult

Multimodal similarity search over embedded assets (ADR-0033). Top-K, no pagination; only embedding_status="ready" assets participate.

Parameters:

Name Type Description Default
parts list[SearchQueryPartParam]

ordered query blocks, each exactly one of {"type": "text", "text": ...}, {"type": "image", "image_uri": ...}, or {"type": "asset_id", "asset_id": ...} (mixed inputs allowed). An asset_id block reuses that asset's stored embedding ("more like this").

required
instruction str | None

optional English retrieval instruction (server picks a default when omitted).

None
filters AssetFilterParam | None

an AssetFilter dict — keys are q (filename substring; NOT filename) / created_at_from / created_at_to / uploaded_by / status / content_type; results are the vector ranking AND the filter.

None
k int

1..200 (default 50).

50
include_total bool

also count filter-matching assets into total.

False

Raises:

Type Description
NotFoundError

an asset_id part references a missing / unembedded asset (ASSET_NOT_FOUND).

ConflictError

the referenced asset lost its stored vector (ASSET_NOT_EMBEDDED).

UnavailableError

the embedder is down or the invocation failed (EMBEDDER_UNAVAILABLE / EMBEDDER_INVOCATION_FAILED).

bulk_delete

bulk_delete(dataset_id: UUID | str, *, asset_ids: list[UUID | str] | None = None, filter: AssetFilterParam | None = None, exclude_asset_ids: list[UUID | str] | None = None, idempotency_key: str | None = None) -> JobHandle

Delete assets in bulk, asynchronously (ADR-0023). Returns a :class:JobHandle (202); call .wait() to poll to completion.

Pass exactly one of asset_ids or filter (an AssetFilter dict: q filename substring / created_at_from / created_at_to / uploaded_by / status / content_type — unknown keys 422). exclude_asset_ids deselects rows from a filter match. For deleting a small explicit selection synchronously, use :meth:batch_delete.

Raises:

Type Description
ConflictError

idempotency key reused with a different body (IDEMPOTENCY_REPLAY).

RateLimitError

per-tenant active-job cap reached (JOB_PER_TENANT_CAP).

UnprocessableError

neither / both of asset_ids and filter.

batch_delete

batch_delete(dataset_id: UUID | str, *, asset_ids: list[UUID | str]) -> AssetBatchDeleteResult

Synchronously delete an explicit selection of assets (1..1000). Idempotent — ids not in the dataset (or already gone) come back skipped. For large or filter-based deletes use :meth:bulk_delete.

Raises:

Type Description
PayloadTooLargeError

more than 1000 ids (use :meth:bulk_delete).

UnprocessableError

empty asset_ids.

upload_paths

upload_paths(dataset_id: UUID | str, paths: list[str | Path], *, concurrency: int = 8) -> list[UploadResult]

Upload local files: batch upload-init → concurrent PUT. Returns one :class:UploadResult per input path, in input order.

The asset rows are created asynchronously server-side after the PUTs land — an ok=True result means the bytes are in S3, not that the asset is queryable yet. Poll :meth:list until the filenames appear ready.

Files over the 100 MB single-part threshold, wrong MIME, or oversized come back as ok=False results (handle large files via :meth:upload_init + :meth:upload_complete). Use unique paths — client_ref is the path.

download_to

download_to(dataset_id: UUID | str, asset_id: UUID | str, path: str | Path) -> Path

Download an asset's bytes to path (parent dirs created). Fetches a fresh presigned URL, then streams from S3 with an auth-less client. Returns the written path.

Raises:

Type Description
NotFoundError

no such asset in this dataset (ASSET_NOT_FOUND).

Response models

Models returned by client.assets methods (fields, types, and what each means).

Image-asset domain models (ADR-0016/0017/0020/0033/0042).

Response shapes for the assets resource: the asset row itself, the three-step presigned-upload envelopes, presigned download/thumbnail URLs, multimodal search hits, and the sync batch-delete result.

AssetSummary is the compact asset reference other domains embed (project asset-state rows) — it lives here, the lowest common module, per the type dependency order; higher modules import it rather than re-declaring it.

UploadResult is a client-side outcome record (not a wire model): one entry per local path handed to assets.upload_paths.

AssetUploadInit module-attribute

AssetUploadInit = Annotated[AssetUploadSinglePart | AssetUploadMultipart, Field(discriminator='upload_type')]

upload-init response — discriminated on upload_type (single | multipart).

AssetProperties

Bases: BaseModel

Extracted image facts (ImageAsset.properties JSONB). {} when extraction soft-failed, so every field is optional; extra="allow" keeps EXIF / future keys through a round-trip.

AssetProjectMembership

Bases: BaseModel

Compact project ref for the asset-list ?include=projects embed.

AssetDownloadUrl

Bases: BaseModel

A short-lived presigned GET URL (content or thumbnail) + its expiry.

MultipartPartUrl

Bases: BaseModel

One presigned PUT for part part_number of a multipart upload.

AssetUploadSinglePart

Bases: BaseModel

upload-init result when the file fits a single PUT: PUT the bytes to upload_url with the exact content_type you declared.

AssetUploadMultipart

Bases: BaseModel

upload-init result for a large file: PUT each part to its parts[].url, then call assets.upload_complete(..., upload_id, s3_key, parts=[{part_number, etag}]). (Not reachable for images today — the 50 MB per-image cap is below the 100 MB multipart threshold — but modelled for contract completeness.)

AssetUploadBatchItemResult

Bases: BaseModel

Per-file outcome in a batch upload-init. Either a presigned URL (upload_type="single") or an error (upload_type="error" with error_code / error_message) — one bad file never poisons the rest. Map back to your input via client_ref (the SDK uses the local path).

error_code="USE_PER_FILE_INIT" means the file exceeds the single-part threshold; fall back to assets.upload_init for that one.

AssetUploadBatchResult

Bases: BaseModel

All per-file results from a batch upload-init, in input order.

Asset

Bases: BaseModel

One image asset. status is the processing lifecycle; a fresh upload is pending until the S3-event worker creates and processes the row (poll assets.list until your filenames appear ready). embedding_status is semantic-search coverage (ADR-0033/0042).

AssetSummary

Bases: BaseModel

Compact asset reference embedded by higher domains (project asset-state rows) to render filename + dimensions without an N+1 lookup. No download URL — callers fetch assets.download_url per asset when rendering.

SearchHit

Bases: BaseModel

One similarity-search result. similarity is cosine in [0, 1] (1 = identical). Carries enough asset fields to render a tile without a follow-up GET (search is dataset-scoped, so no project tags).

SearchResult

Bases: BaseModel

Top-K search response (no pagination). total is populated only when include_total=True was passed — it counts assets matching the filter predicate, ignoring vector ranking.

AssetBatchDeleteResultItem

Bases: BaseModel

One row of the sync batch-delete outcome: deleted (removed now) or skipped (not in this dataset / already gone — batch-delete is idempotent).

AssetBatchDeleteResult

Bases: BaseModel

Aggregate + per-id results of a sync batch-delete.

UploadResult dataclass

UploadResult(path: str, ok: bool, s3_key: str | None = None, error: str | None = None)

Client-side outcome for one local path handed to assets.upload_paths.

ok is True once the bytes were PUT to S3 successfully. Note the asset row is created asynchronously server-side afterwards — poll assets.list until the filename appears ready before treating it as ingested. error carries the init rejection (bad MIME, oversized) or the PUT exception string when ok is False.

parse_upload_init

parse_upload_init(payload: dict[str, Any]) -> AssetUploadSinglePart | AssetUploadMultipart

Validate a raw upload-init payload into the right union member.