Record with provenance
A studio's outputs belong in helmstudio's library, with what made them. Two calls do it: adopt the file the studio wrote, then record a gallery item that holds it, with the parameters that made it and the inputs it was made from. Every item made from another item can then be traced both ways.
This studio holds assets and gallery:
"""Record an output with the inputs that made it, then walk its provenance.
Run under helmstudio or helm dev by a studio holding `assets` and `gallery`.
"""
import os
import struct
import zlib
from helm_runtime_sdk import from_env
helm = from_env()
def solid_png(r, g, b, size=8):
"""A small single-colour PNG, standing in for what a model would make."""
row = b"\x00" + bytes([r, g, b]) * size
def chunk(kind, data):
body = kind + data
return struct.pack(">I", len(data)) + body + struct.pack(">I", zlib.crc32(body) & 0xFFFFFFFF)
header = struct.pack(">IIBBBBB", size, size, 8, 2, 0, 0, 0)
return (b"\x89PNG\r\n\x1a\n" + chunk(b"IHDR", header)
+ chunk(b"IDAT", zlib.compress(row * size)) + chunk(b"IEND", b""))
def adopt_png(name, rgb):
"""Write a file to the stage directory and adopt it: no copy, no upload."""
path = os.path.join(os.environ["HELM_STAGE_DIR"], name)
with open(path, "wb") as f:
f.write(solid_png(*rgb))
return helm.assets.adopt({"path": path, "kind": "image"})
# A first generation: a reference image, recorded with what made it.
reference = adopt_png("reference.png", (200, 120, 40))
first = helm.gallery.add({
"kind": "image",
"asset_id": reference["id"],
"title": "a lighthouse at dusk",
"params": {"prompt": "a lighthouse at dusk", "seed": 7, "steps": 4},
})
# A second, made from the first: its input names the asset and the role it
# played. That link is the provenance.
variation = adopt_png("variation.png", (40, 120, 200))
second = helm.gallery.add({
"kind": "image",
"asset_id": variation["id"],
"title": "the same lighthouse, at night",
"params": {"prompt": "the same lighthouse, at night", "seed": 8, "strength": 0.6},
"inputs": [{"asset_id": reference["id"], "role": "reference"}],
"tags": ["variation"],
})
# Upstream: the items whose outputs the second was made from.
upstream = helm.gallery.lineage(second["id"])
assert [i["id"] for i in upstream["items"]] == [first["id"]], upstream
# Downstream: every item ever made from the reference asset.
downstream = helm.assets.lineage(reference["id"])
assert second["id"] in [i["id"] for i in downstream["items"]], downstream
print("ok")
Adopt, don't upload
A studio writes its output to HELM_STAGE_DIR, a directory helmstudio gives each run, and adopts it from there. Adopting hardlinks the file into the asset store: no bytes are copied and nothing is sent over HTTP, so adopting a long video costs no more than adopting a small image. A file on a different volume from the store cannot be hardlinked, and adopting it is refused rather than quietly copied.
A studio can also adopt from its own data directory, {data}. A file left in the stage directory unadopted is removed when the studio's processes stop.
Assets are deduplicated by content: adopting the same bytes twice gives back the same asset.
Record the item
An item names its asset, its kind, and params — whatever made it, in the studio's own terms. inputs is the provenance: each input names an asset and the role it played, such as a reference image or a first frame. The role is the studio's word; helmstudio keeps it and shows it.
Walk the lineage
gallery.lineage(item)goes upstream: the items whose outputs this one was made from, recursively.assets.lineage(asset)goes downstream: every item made from that asset.
Both leave out items another studio made unless the caller holds gallery.read_all, whose sentence on the approval screen is a warning for exactly that reason. See capabilities.