Assets¶
Assets are how runs produce durable outputs (checkpoints, reports, datasets, environment snapshots) and how researchers attach research documents to experiments. methodic supports two upload paths depending on size and shape, plus convenience helpers on Run and chronicle.assets.
Inline (small structured payloads)¶
For research reports, environment snapshots, and other small payloads, post the content inline and Chronicle auto-finalizes:
# Worker side — auto-populates output_of from the bound run
run.upload_asset(
asset_type="takeaways_report",
content={
"summary": "Loss converged at step 12000",
"key_findings": [...],
},
)
# Researcher side — explicit (no run context)
chronicle.assets.create_inline(
asset_type="research_report",
content={"notes": "..."},
)
Reserved asset types Chronicle knows about:
hypothesis_report— full hypothesis document, submitted at experiment creationtakeaways_report— post-experiment analysisresearch_report— general research documents (literature review, synthesis)
You can use any other string for custom asset types.
Markdown / plain text¶
For human-readable docs (research reports, notes), pass the text as content and override the content type:
hypothesis = open("hypothesis.md").read()
chronicle.assets.create_inline(
asset_type="hypothesis_report",
content=hypothesis,
content_type="text/markdown",
)
Binary (presigned URL flow)¶
For checkpoints, large datasets, or anything you'd rather not pipe through Chronicle, the upload goes directly to cloud storage. Three steps:
# 1. Register the asset and get presigned URLs (one per component)
upload_info = run.create_asset_presigned(
asset_type="checkpoint",
components=["model.safetensors", "optimizer.pt", "metadata.json"],
)
# 2. PUT each component to its presigned URL
for filename, url in upload_info.upload_urls.items():
run.upload_component(url, local_path=Path(f"./out/{filename}"), content_type="application/octet-stream")
# 3. Finalize — asset transitions to `ready` and becomes immutable
run.finalize_asset(upload_info.asset_id)
For convenience, run.upload_directory_async() does all three steps for a directory of files in the background.
Asset states¶
| State | Meaning |
|---|---|
pending |
Created, components not all uploaded yet |
ready |
Finalized, immutable, available as an input to other variations |
abandoned |
Run failed before finalize; storage may be GC'd |
Validity flags¶
Chronicle may mark an asset (set them yourself under Managing assets):
- deprecated — soft warning, asset still usable
- invalidated — hard block; Chronicle won't accept it as a new input without
allow_invalid_assets: true. Outputs of a retracted experiment are invalidated automatically.
These flags don't change the lifecycle state; they're orthogonal annotations.
Downloading¶
Single GET request: chronicle.assets.get(asset_id, include_presigned=True) returns metadata + presigned read URLs in one call, which download consumes. Files stream straight to disk.
Managing assets¶
Beyond upload and download, chronicle.assets carries an asset's validity annotations, access control, and ownership. (Run-output approval — approve / reject / bulk_approve — lives in the attention & review guide.)
Deprecate, invalidate, delete¶
Three escalating ways to take an asset out of use, matching the validity flags above:
chronicle.assets.deprecate(asset_id, reason="superseded by the Re=2000 sweep") # soft warning, still usable
chronicle.assets.invalidate(asset_id, reason="labeling bug in the source data") # hard block as a new input
chronicle.assets.delete(asset_id) # hard delete — only if unlinked
deprecate leaves the asset usable but surfaces reason wherever it's linked ("superseded, but existing results stand"). invalidate blocks it as a new input — link it anyway with allow_invalid_assets=True — while keeping the row, links, and provenance ("wrong data, do not build on this"). delete is the orphan-cleanup path: it removes the row, ACLs, bytes, and search document, and is refused with 409 (asset_linked) while any experiment or variation references it. Reach for deprecate/invalidate on assets that are part of an experiment's record; delete only over-uploaded or abandoned orphans.
Access control¶
Assets carry generic ACLs — datasets are plain assets, so the same calls apply:
chronicle.assets.grant_access(asset_id, principal_id="user_abc", action="read") # read | write | delete | administer
chronicle.assets.revoke_access(asset_id, principal_id="user_abc", action="read")
chronicle.assets.list_access(asset_id) # who can reach it
chronicle.assets.share_with_scope(asset_id, scope_id="team_9") # grant a whole team/org read
principal_id is a user sub, a team/org id, or everyone; resolve team/org ids with chronicle.me.scopes(). grant_access/revoke_access are idempotent and require Administer on the asset (its creator has it). share_with_scope is a thin convenience over grant_access for sharing with an entire scope.
Visibility and ownership¶
set_visibility sets the asset's broadcast read grant independent of any experiment it's linked to; move transfers it into an organization (the asset analog of experiments.move):
chronicle.assets.set_visibility(asset_id, "public") # "private" | "organization" / "team" | "public"
chronicle.assets.move(asset_id, organization_id="org_123", visibility="organization")
move requires Administer on the asset and membership of the target org; created_by is unchanged.