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".
Tag a version
Section titled “Tag a version”from qualia import Qualia
client = Qualia()
diff = client.versions.compare("<DATASET_UUID>") # newest tag → headtag = 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.
Download a version
Section titled “Download a version”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.
| Argument | Description |
|---|---|
version | <dataset>:<tag>, a full version name, or a tag plus dataset=. head is refused: tag it first |
dest | The folder to create; its parent is created if missing. Default: <dataset>-<tag> here, plus -v21 or -mcap |
layout | lerobot_v3 (default), lerobot_v2_1, or mcap_ros2 (ROS 2 MCAP, one .mcap per episode) |
on_progress | Called 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 |
timeout | Seconds to wait for the rendering |
poll_interval | Seconds between reads while it renders (default 2) |
parallel | Files 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.
One step at a time
Section titled “One step at a time”m = client.versions.start_materialization("my-dataset:1.0", layout="lerobot_v2_1")m = client.versions.get_materialization(m.name) # NOT_STARTED, RUNNING, SUCCEEDED, INTERRUPTEDpage = 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_timeMerge history
Section titled “Merge history”history = client.versions.merge_history("<MERGED_DATASET_UUID>")for entry in history.entries: print(entry.create_time, entry.kind, len(entry.datasets))Curation record
Section titled “Curation record”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.