provevi.sqlite
sqlite store: the evidence of many runs in one SQLite file.Each event becomes a row of provevi_events as it is written, with its URIs
in provevi_event_refs. A run has a row in provevi_runs from its start,
and its manifest at the seal. Grants, refusals, inputs and outputs are views
over the events, so a query across runs, such as “which runs read this URI”,
is one SELECT.
The store also writes the files of the file store into each run’s
evidence folder: events.jsonl, payloads/ and manifest.json.
provrun commits that folder as the bundle, so a run’s bundle is the same in
both stores. The payload bytes live only in that folder; the table records
their hash, size and place.
The tables are provevi’s own, and their names start with provevi_, so the
file can also hold MLflow’s tables. The file records its schema version, and
opening it to write brings an older schema up to date. Opening it to read,
as prove does, changes nothing.
A run is an instance of an agent class, its agent’s name with the run’s org, domain and environment, and of a version of that class, the SHA-256 of its spec without the prompt and the inputs. A run may also name an experiment, which is a catalog object; the file keeps each version of it that a run reported, by its pin and its file’s hash.
The check of a run covers each event’s line, and every column that the events imply: the run’s org, domain, environment, actor, mode, agent class and version, experiment and its pin, status and times, its settings, the span IDs and the payload sizes. It does not cover the run’s folder, its sealed_at time, or the first-run details of an agent class or an experiment version (the first run, created_by, created_at), which the first run fixes.
class SqliteStore
SqliteStore(path: str | Path, *, create: bool = True)The evidence of many runs in one SQLite file, and the files of each run in its evidence folder.
Two runs, in one process or in two, can write the same file at once. A store opened to read raises ProveviError for a missing file, a file that is not provevi’s, or one of another schema version, and writes nothing. Opening the file and reading it turn every SQLite error into a ProveviError; a write can still raise sqlite3.Error.
| Parameter | Type | Default | Description |
|---|---|---|---|
path |
str | Path | required | The SQLite file; ~ is the home folder, and a relative path is from the current folder |
create |
bool | True |
Make the file, or bring its schema up to date, to write runs; False only reads |
SqliteStore.open_run
SqliteStore.open_run(run_id: str, folder: Path) -> SqliteRunWriterOpen a new run in the file and in its evidence folder.
Raises ProveviError for a store opened to read, and EvidenceError for a run already in the file.
| Parameter | Type | Default | Description |
|---|---|---|---|
run_id |
str | required | The new run’s ID; it must not be in the file yet |
folder |
Path | required | The run’s evidence folder, where the files of the file store go too |
| returns | SqliteRunWriter | The writer of the run, to the file and the folder |
SqliteStore.read_run
SqliteStore.read_run(run_id: str, folder: Path | None = None) -> SqliteRecordRead a run back from the file. Raises RunNotFound when the file has no such run.
| Parameter | Type | Default | Description |
|---|---|---|---|
run_id |
str | required | The run’s ID |
folder |
Path | None | None |
The evidence folder to read the payloads from; None for the place the file records |
| returns | SqliteRecord | The run as the file holds it |
SqliteStore.list_runs
SqliteStore.list_runs() -> list[str]List the runs in the file.
| Parameter | Type | Default | Description |
|---|---|---|---|
| returns | list[str] | The run IDs, sorted, so oldest first |
SqliteStore.run_folder
SqliteStore.run_folder(run_id: str) -> Path | NoneThe run folder that the file records.
The check does not cover it, so prove looks under the runs root first.
| Parameter | Type | Default | Description |
|---|---|---|---|
run_id |
str | required | The run’s ID |
| returns | Path | None | The folder; None when the file has no such run |
SqliteStore.runs_reading
SqliteStore.runs_reading(uri: str) -> list[str]List the runs that staged a URI as an input.
A run counts if it staged the URI or a pinned version of it, which is the URI followed by ?.
| Parameter | Type | Default | Description |
|---|---|---|---|
uri |
str | required | A logical URI, or a pinned version of it |
| returns | list[str] | The run IDs, sorted |
SqliteStore.runs_of_experiment
SqliteStore.runs_of_experiment(experiment: str) -> list[str]List the runs that named an experiment.
Its URI or its name, such as acme/risk/dev/credit/q, gives the runs of every version; a pinned URI gives the runs of that version.
| Parameter | Type | Default | Description |
|---|---|---|---|
experiment |
str | required | An experiment’s URI or name, or a pinned URI for one version |
| returns | list[str] | The run IDs, sorted |
SqliteStore.runs_of_agent
SqliteStore.runs_of_agent(agent: str, version: str | None = None) -> list[str]List the runs of an agent class, or of one version of it.
| Parameter | Type | Default | Description |
|---|---|---|---|
agent |
str | required | An agent class, such as acme/demo/dev/revenue-agent |
version |
str | None | None |
The SHA-256 of a version’s spec, or the start of it; None for every version |
| returns | list[str] | The run IDs, sorted |
SqliteStore.experiments
SqliteStore.experiments() -> list[dict]List the experiments that runs named, with their numbers of versions and of runs.
| Parameter | Type | Default | Description |
|---|---|---|---|
| returns | list[dict] | id, uri, first_run, created_at, versions and runs of each, by ID |
SqliteStore.experiment
SqliteStore.experiment(experiment: str) -> dict | NoneDescribe an experiment, with each version that runs reported, oldest first, and its runs.
| Parameter | Type | Default | Description |
|---|---|---|---|
experiment |
str | required | An experiment’s URI or name; a selector is ignored |
| returns | dict | None | id, uri, first_run, created_at and versions, each with pin, sha256, first_run, created_at and runs; None when no run named it |
SqliteStore.agents
SqliteStore.agents() -> list[dict]The agent classes, each with its number of versions and of runs.
| Parameter | Type | Default | Description |
|---|---|---|---|
| returns | list[dict] | id, first_run, created_at, versions and runs of each, by ID |
SqliteStore.agent
SqliteStore.agent(agent: str) -> dict | NoneDescribe an agent class, with each version, oldest first, and its runs.
| Parameter | Type | Default | Description |
|---|---|---|---|
agent |
str | required | An agent class: org/domain/env/name |
| returns | dict | None | Its row and versions, each with spec_sha256, settings without secrets, first_run, created_at and runs; None when there is none |
SqliteStore.run
SqliteStore.run(run_id: str) -> dict | NoneThe run’s row, and its settings as params.
| Parameter | Type | Default | Description |
|---|---|---|---|
run_id |
str | required | The run’s ID |
| returns | dict | None | The columns of the run’s row and params; None when there is no such run |
SqliteStore.access
SqliteStore.access(run_id: str) -> list[dict]The grants and refusals of a run, in the order they happened.
| Parameter | Type | Default | Description |
|---|---|---|---|
run_id |
str | required | The run’s ID |
| returns | list[dict] | seq, time, decision (granted or denied), uri, mode and reason of each |
SqliteStore.inputs
SqliteStore.inputs(run_id: str) -> list[dict]The inputs a run staged, in order.
| Parameter | Type | Default | Description |
|---|---|---|---|
run_id |
str | required | The run’s ID |
| returns | list[dict] | seq, uri, pinned, sha256 and agent_path of each |
class SqliteRunWriter
SqliteRunWriter(store: SqliteStore, run_id: str, folder: Path)Writes one run to the file and to the run’s evidence folder.
An event counts as written when both hold it: the row is committed only after the line is in events.jsonl, and if the commit fails, the line is cut from the file again. If the process dies between the two, the file holds one more line than the rows, and the check against the store reports it.
| Parameter | Type | Default | Description |
|---|---|---|---|
store |
SqliteStore | required | The store to write to |
run_id |
str | required | The new run’s ID; a run already in the file raises EvidenceError |
folder |
Path | required | The run’s evidence folder |
SqliteRunWriter.append
SqliteRunWriter.append(line: str) -> NoneWrite an event’s rows and its line in events.jsonl, both or neither.
Raises EvidenceError once the run is sealed.
| Parameter | Type | Default | Description |
|---|---|---|---|
line |
str | required | One event line, with no newline |
SqliteRunWriter.close
SqliteRunWriter.close() -> NoneClose events.jsonl.
SqliteRunWriter.put_payload
SqliteRunWriter.put_payload(data: bytes | str) -> strStore a payload once in the run folder’s payloads/, and record its hash, size and place.
| Parameter | Type | Default | Description |
|---|---|---|---|
data |
bytes | str | required | The payload; a text is stored as UTF-8 |
| returns | str | The payload’s SHA-256 |
SqliteRunWriter.get_payload
SqliteRunWriter.get_payload(digest: str) -> bytesRead a payload from the run folder’s payloads/.
| Parameter | Type | Default | Description |
|---|---|---|---|
digest |
str | required | The payload’s SHA-256 |
| returns | bytes | The payload, from the run folder |
SqliteRunWriter.payload_names
SqliteRunWriter.payload_names() -> list[str]The payloads of the run, as the file records them.
| Parameter | Type | Default | Description |
|---|---|---|---|
| returns | list[str] | The SHA-256 of each payload the file records, sorted |
SqliteRunWriter.seal
SqliteRunWriter.seal(manifest: dict) -> NoneWrite manifest.json and record the manifest in the run’s row. No write comes after.
| Parameter | Type | Default | Description |
|---|---|---|---|
manifest |
dict | required | The manifest the recorder made |
SqliteRunWriter.manifest
SqliteRunWriter.manifest() -> dictThe manifest recorded by seal. Raises EvidenceError before the seal.
| Parameter | Type | Default | Description |
|---|---|---|---|
| returns | dict | The manifest the file records |
SqliteRunWriter.record_bundle
SqliteRunWriter.record_bundle(uri: str | None, pinned: str | None) -> NoneRecord where the run’s bundle was committed, in the run’s row.
| Parameter | Type | Default | Description |
|---|---|---|---|
uri |
str | None | required | The URI the bundle was committed at; None when it was not committed |
pinned |
str | None | required | The URI pinned to the commit; None when it was not committed |
class SqliteRecord
SqliteRecord(store: SqliteStore, run_id: str, folder: Path | None = None)A run as the file holds it, for the checks.
Besides the chain, it checks every column that the events imply, so a changed column fails as a changed line does. A file it cannot read raises EvidenceError.
| Parameter | Type | Default | Description |
|---|---|---|---|
store |
SqliteStore | required | The store that holds the run |
run_id |
str | required | The run’s ID |
folder |
Path | None | None |
The evidence folder to read the payloads from; None for the place the file records |
SqliteRecord.lines
SqliteRecord.lines() -> list[str]The event lines, in order. Raises EvidenceError if they cannot be read.
| Parameter | Type | Default | Description |
|---|---|---|---|
| returns | list[str] | The line of each event row, by seq |
SqliteRecord.manifest_bytes
SqliteRecord.manifest_bytes() -> bytes | NoneThe manifest as the file holds it.
| Parameter | Type | Default | Description |
|---|---|---|---|
| returns | bytes | None | The manifest the run’s row holds, in UTF-8; None before the seal |
SqliteRecord.payload_digest
SqliteRecord.payload_digest(name: str) -> str | NoneHash the payload file called name, if the file records the payload. May raise OSError.
| Parameter | Type | Default | Description |
|---|---|---|---|
name |
str | required | The payload’s SHA-256, as an event names it |
| returns | str | None | The SHA-256 of its file’s bytes; None when the file does not record it or its file is missing |
SqliteRecord.assessments
SqliteRecord.assessments() -> list[dict]The assessments of this run, and of its spans, in the order they were made.
| Parameter | Type | Default | Description |
|---|---|---|---|
| returns | list[dict] | id, target_kind, target_id, name, value, rationale, source, created_by and created_at of each |
SqliteRecord.payload_bytes
SqliteRecord.payload_bytes(name: str) -> bytes | NoneRead the payload called name, if the file records it.
| Parameter | Type | Default | Description |
|---|---|---|---|
name |
str | required | The payload’s SHA-256 |
| returns | bytes | None | Its file’s bytes; None when the file does not record it or it is not a regular file |
SqliteRecord.payload_files
SqliteRecord.payload_files() -> tuple[set[str], list[str]]The payloads the file records for the run.
| Parameter | Type | Default | Description |
|---|---|---|---|
| returns | tuple[set[str], list[str]] | The digests, and no problems |
SqliteRecord.problems
SqliteRecord.problems() -> list[str]Find the rows that do not match their line, and the stored values that differ from what the events imply.
| Parameter | Type | Default | Description |
|---|---|---|---|
| returns | list[str] | One line each; empty when every row matches |