Skip to content

Versions

A version is a tag: a name such as 1.0 on what a dataset yielded at one moment. A tag never moves. Besides tag names, stable (the newest tag), head (what the dataset yields now) and a digest prefix resolve too. Uploads, merged datasets and mixtures all have versions.

client.versions is on the qualia.v1 Versions API, which every key that can read the dataset reaches. A key restricted to workspaces is refused.

Methods take a version’s full name (accounts/<account>/datasets/<dataset>/versions/<tag>) or a tag plus dataset=. download, the methods that prepare a version and curation_record also take <dataset>:<tag>, with the dataset’s slug or id: a version’s reference, such as "my-dataset:1.0".

from qualia import Qualia
client = Qualia()
diff = client.versions.compare("<DATASET_UUID>") # newest tag → head
tag = client.versions.create(
"<DATASET_UUID>",
diff.suggested_version,
note=diff.draft_note,
digest=diff.target_digest, # refused (409) if head moved since
)
print(tag.reference, tag.dataset_version_id)

list(dataset) pages the tags newest first, get(tag, dataset=) reads one, and update(tag, dataset=, note=) edits its note.

result = client.versions.download("my-dataset:1.0", "./my-dataset-1.0")
print(result.destination, result.file_count, result.byte_count)

download starts the rendering, or picks up the one already there, and waits until it is done (timeout=, default 30 minutes; it keeps rendering after a timeout). It then fetches every file from its signed link into .<name>.qualia-version-partial beside dest, checks each one’s size and sha256 against the listing, and renames the folder to dest. dest never holds part of a version. The API key is never sent to a signed link.

ArgumentDescription
version<dataset>:<tag>, a full version name, or a tag plus dataset=. head is refused: tag it first
destThe folder to create; its parent is created if missing. Default: <dataset>-<tag> here, plus -v21 or -mcap
layoutlerobot_v3 (default), lerobot_v2_1, or mcap_ros2 (ROS 2 MCAP, one .mcap per episode)
on_progressCalled with {"step": "preparing", "state": ..., "version": "<slug>:<tag>"} while the server builds the version, then {"step": "downloading", "files_done", "files_total", "bytes_done", "bytes_total"} as each file arrives
timeoutSeconds to wait for the rendering
poll_intervalSeconds between reads while it renders (default 2)
parallelFiles fetched at once (default 4)

It returns a VersionDownload: destination, version, materialization (its digest names the exact files), layout, file_count, byte_count, downloaded_file_count and reused_file_count. to_wire() gives it as JSON.

A call that stops keeps the files it checked; the same call again checks them and fetches only the rest. When dest already holds exactly this rendering, the call checks it and fetches nothing. Any other existing dest raises FileExistsError. A file whose bytes do not match raises QualiaError and nothing is published.

Preparing a version in a format the first time needs data:write; once it is prepared, any key that can read the dataset downloads it. To download by dataset name, with the newest version as the default, use client.data.download.

m = client.versions.start_materialization("my-dataset:1.0", layout="lerobot_v2_1")
m = client.versions.get_materialization(m.name) # NOT_STARTED, RUNNING, SUCCEEDED, INTERRUPTED
page = client.versions.list_materialization_files(m.name, limit=512)
for f in page.items:
print(f.path, f.size_bytes, f.sha256, f.uri) # uri is valid until f.expire_time
history = client.versions.merge_history("<MERGED_DATASET_UUID>")
for entry in history.entries:
print(entry.create_time, entry.kind, len(entry.datasets))

What was done to a dataset in the round a version opened: from its creation to the next tag’s (for head, since the newest tag). This is the dashboard’s Curation of 1.0 panel.

record = client.versions.curation_record("my-dataset:1.0")
for event in record.events: # oldest first
print(event.create_time, event.actor.display_name, event.summary)
print(record.start_time, record.end_time, record.truncated)

A merged dataset’s record also reads the uploads it holds, from when its merge took each in. At most 2000 events are read; a longer round keeps its newest and sets truncated. The events come from the dataset’s workspace.