Skip to content

Storage / Sinks / Usage

Three more URI-scheme backend groups, each a small protocol with interchangeable implementations chosen by config (② provider-agnostic). The generation and export code is identical across them.

Blob store (core/storage/)

Where rendered documents and golden shards land. open_store(uri)file:// · gs:// · s3://.

class BlobStore(Protocol):
    def put(self, key, data, content_type="application/octet-stream") -> str: ...
    def get(self, key) -> bytes: ...
    def exists(self, key) -> bool: ...
    def iter_keys(self, prefix="") -> Iterator[str]: ...
    def uri_for(self, key) -> str: ...

Keys are deterministic and unit-sharded, e.g. <prefix>/documents/unit-000123/<id>.pdf and <prefix>/golden/<table>/unit-000123.jsonl.gz, where <prefix> defaults to the run id (a multi-slice run nests under runs/<run>/<slice>/…). iter_keys is what export and manifest verification walk. Cloud backends need the matching extra (docsynth[gcp] / [aws]); a missing one raises an actionable error naming the install.

Golden sinks (core/sinks/)

Export targets. open_sink(uri)parquet:// · duckdb:// · bigquery://.

class GoldenSink(Protocol):
    def write(self, table, rows) -> str: ...   # append rows to a table
    def register(self) -> None: ...            # make the tables queryable (views / external tables)
    def close(self) -> None: ...

export_run calls write per shard in key order (deterministic Parquet parts) and register once at the end. Rows are plain dicts converted through Arrow; money stays exact (decimal, not float). DuckDB adds views over the Parquet; BigQuery stages Parquet in GCS and creates external tables; plain Parquet's register is a no-op. See Export.

Usage telemetry (core/usage/)

Records what every LLM call cost — model, tokens, exact Decimal cost, and what it was generating — as its own llm_usage table. On by default (a procedural pack makes no calls, so it writes nothing). open_usage_sink(uri)shard:// (default, gzipped-JSONL beside the golden data) · firestore:// · dynamodb:// · off.

class UsageSink(Protocol):
    def record(self, usage: LlmUsage) -> None: ...
    def flush(self) -> int: ...
    def close(self) -> None: ...

It is deliberately not golden data — token counts aren't reproducible, and the golden record's value is that it is exactly recomputable. A replayed unit overwrites its rows (shard key / deterministic id / sort key), so retries can't double-count. Because export_run discovers tables by prefix, the shard:// usage exports to Parquet/DuckDB/BigQuery with no export change.

Distinct from the spend rollup in the state store: the rollup is a live per-(run, model) counter a budget guard reads across a fleet; llm_usage is the analytics fact table. The two agree on how a run is identified.