Official Python SDK for CAVS Hub — the deduplicating, content-addressed artifact store for AI, CI/CD and game pipelines.
Project description
cavs — Python SDK for CAVS Hub
Official Python client for CAVS Hub, the
content-addressed, deduplicating artifact store for AI, CI/CD and game pipelines.
It speaks the same canonical API the CLI uses — one API, one permission model, one
data plane. The wire behaviour is defined in ../CONTRACT.md and
pinned by the shared vectors in ../spec/contract/.
- Requires Python 3.9+.
- Only runtime dependency:
httpx.
Install
pip install cavs
From this monorepo checkout:
cd python
pip install -e ".[dev]"
Quickstart
from cavs import CAVS
client = CAVS.from_env() # reads CAVS_TOKEN + CAVS_API
artifact = client.artifacts.upload(
path="./model",
project="vision-models",
name="resnet50",
kind="model",
version="1.4.0",
metadata={"framework": "pytorch", "accuracy": 0.942},
)
print(artifact.reference) # cavs://acme-ai/vision-models/model/resnet50:1.4.0
print(artifact.deduplication_ratio)
client.artifacts.download(artifact.reference, dest="./restored")
Track training runs and lineage:
run = client.runs.create(project="vision-models", name="nightly", system="mlflow")
client.runs.update("vision-models", run.id, status="completed")
Authentication
Create a service account in the CAVS dashboard and export its key. Tokens are
resolved in this order: an explicit token= argument, then $CAVS_TOKEN.
export CAVS_TOKEN=cavs_sk_...
export CAVS_API=https://api.cavs.dev # default; use http://localhost:8080 for dev
Other environment variables:
| Variable | Purpose |
|---|---|
CAVS_TOKEN |
Auth token (cavs_sk_…, cavs_pat_…, …) |
CAVS_API |
Base URL (default https://api.cavs.dev) |
CAVS_ORG |
Default organization slug |
CAVS_CA_BUNDLE |
Path to a custom CA bundle for TLS verification |
Or construct explicitly:
client = CAVS(token="cavs_sk_...", api="https://api.cavs.dev", org="acme-ai")
project and org resolution
project is an alias for a repository. It is resolved within an org by
matching the value against each repository's id, slug or name
(GET /organizations/{org}/repositories); if nothing matches, the value is used
verbatim as a repository id. The org comes from an explicit argument, then
CAVS_ORG, then the caller's default org from GET /users/me.
Security
Following ../CONTRACT.md §2, this SDK:
- never prints the token in
repr, exceptions, or logs — it is redacted to e.g.cavs_sk_…abcd; - never logs presigned URLs or full request headers;
- verifies TLS and honours
CAVS_CA_BUNDLE; caps redirects at 5; - always sets connect + read timeouts;
- verifies
sha256 == oidon every download and refuses to write a mismatched file (ChecksumError), cleaning up the temp file; - guards against path traversal when expanding directories / archive members;
- honours
Retry-Afterand cleans up temp files on failure; - supports cancellation via a
threading.Event(or any object withis_set()).
Uploads and downloads
Large files never travel through the control plane — they stream directly to object storage via presigned URLs (CONTRACT §5).
- Upload: hash every file (streamed sha256, 64 KiB chunks) → open a session
with an
Idempotency-Key→ authorize objects (the Hub reports which alreadyexists, so dedup and resume are automatic) →PUTthe missing bytes →completeeach object →finalize(idempotent), which returns dedup stats. - Download: authorize → stream to a deterministic temp file while hashing →
verify
sha256 == oid→ atomically rename into place.
Progress and cancellation:
import threading
cancel = threading.Event()
client.artifacts.upload(
path="./big-dataset",
project="datasets",
name="imagenet",
kind="dataset",
version="2026.07.24",
on_progress=lambda done, total: print(f"{done}/{total}"),
cancel=cancel,
)
cavs:// URIs
from cavs import parse, format_uri
p = parse("cavs://acme-ai/vision-models/model/resnet50:1.4.0")
p.org, p.name, p.version # 'acme-ai', 'resnet50', '1.4.0'
format_uri(p) # round-trips to the canonical form
Errors
All exceptions derive from cavs.CAVSError. The HTTP → exception mapping follows
CONTRACT §8:
| HTTP | Exception |
|---|---|
| 401 | AuthenticationError |
| 403 | AuthorizationError |
| 404 | NotFoundError |
| 409 | ConflictError |
| 413 / 402 | QuotaExceededError |
| 429 | RateLimitError (honours Retry-After) |
| 5xx / network | TemporaryServiceError (retried, ≤4 attempts) |
| client-side checksum mismatch | ChecksumError |
| upload / download transport | UploadError / DownloadError |
from cavs import CAVS, NotFoundError, RateLimitError
try:
client.artifacts.get("does-not-exist")
except NotFoundError as e:
print(e.code, e.status)
except RateLimitError as e:
print("retry after", e.retry_after)
Development
cd python
python -m venv .venv && . .venv/bin/activate
pip install -e ".[dev]"
ruff check . && mypy src && pytest --cov=cavs --cov-fail-under=90
Tests mock httpx with respx — no network is
used. See ../CONTRIBUTING.md and the docs in
../docs/.
License
Apache 2.0 — see ../LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file cavs-0.1.0.tar.gz.
File metadata
- Download URL: cavs-0.1.0.tar.gz
- Upload date:
- Size: 26.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1827033830e476c62b33da7e81569d542612b0244c98c71375030c11169c8d81
|
|
| MD5 |
75d4dce2498002044a059ba4c8536717
|
|
| BLAKE2b-256 |
160ca63c2a126e39ded3f659ae08e08819adb3b75d326cf475a852f69425a7d0
|
File details
Details for the file cavs-0.1.0-py3-none-any.whl.
File metadata
- Download URL: cavs-0.1.0-py3-none-any.whl
- Upload date:
- Size: 25.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6baf50092a9709e37b8feb7d4100b6aea9089593c55da31d7a08f8fe78e3fba7
|
|
| MD5 |
6ba1885fb6c2edbcd7dedb0562a9afa6
|
|
| BLAKE2b-256 |
216b04cda18c365f7de30b0e98996088b302f2ee740f6a30e7049b676d380585
|