docs
Python SDKReference

Store

API reference for the distributed object store

API reference for urun.store — the distributed object store.

Public API at a Glance

NameDescription
app.storeApp-bound store — auto org+app-scoped cache keys
store.model(url, ...)Download + load a HuggingFace model in one call
store.repo(url, ...)De-vendor an upstream repo at a pinned commit
store.get(key, ...)Load content by key
store.set(key, value)Store named key-value data
store.put(obj)Store ephemeral object, get ref handle
store.path(key)Get filesystem path for key
store.lookup(pointer_key, ...)Resolve pointer to latest version
store.stream(name, ...)Create global stream
store[key] / store[key] = val / del store[key]Dict-style access
key in storeMembership test
@store.cache()Cache model-loading results, keeping them hot in GPU memory
store.cache_stats()Get cache statistics
store.clear_cache()Clear local cache
store.configure_cache()Configure cache settings

For patterns and narrative explanation, see the Store concept page.


app.store

app = App("liveavatar")
store = app.store

Returns a store proxy bound to the app. Its only behavioral change versus from urun import store is that @store.cache(key=...) keys are automatically namespaced with the owning org and the app name ("<org>:<app>:<key>"), so two tenants deploying the same app with the same bare key get distinct cache namespaces.

Everything else delegates unchanged to the shared store. Critically, store.model / store.path / store.repo stay content-addressed and shared across apps and orgs — only the per-app cache-key namespace is scoped. The org segment is sourced from the runtime/deploy context; you never pass it by hand. (Outside a deploy — local/dev — there is no org to scope to, so the cache key is app-scoped only and a one-time warning is logged.)


store.model()

store.model(
    url: str,
    *,
    cls: type | None = None,
    subfolder: str | None = None,
    device: str | None = None,
    device_map: str | None = None,
    dtype: Any | None = None,
    allow_patterns: list[str] | None = None,
    ignore_patterns: list[str] | None = None,
    **from_pretrained_kwargs,
) -> Any

Download and load a HuggingFace model in one call — download (with auth), auto-detect the model class, load via from_pretrained, and place on device. Content-addressed and shared across apps and orgs (like store.path / store.repo).

ParameterTypeDefaultDescription
urlstrrequiredHuggingFace URI, e.g. "hf://Wan-AI/Wan2.1-T2V-14B"
clstype | NoneNoneModel class. None auto-detects from model_index.json (diffusers) or config.json (transformers); pass explicitly for research models without standard metadata
subfolderstr | NoneNoneLoad a sub-component (e.g. "text_encoder")
devicestr | NoneNoneTarget device ("cuda", "cuda:0"); ignored when device_map is set
device_mapstr | NoneNoneAccelerate device map (e.g. "auto") for multi-GPU sharding / CPU-disk offload
dtype--NoneTorch dtype for the load
allow_patterns / ignore_patternslist[str] | NoneNoneRestrict which files download
pipe = store.model("hf://org/restyle-14b", device=str(ctx.device), dtype=torch.bfloat16)

For the canonical loader shape, wrap the store.model call in @store.cache(key=...) so the loaded object stays resident across sessions.


store.repo()

store.repo(
    url: str,
    *,
    include: list[str] | None = None,
    patches: list[tuple[str, str, str]] | None = None,
    diff: str | list[str] | None = None,
    overlay: str | dict[str, bytes | str] | None = None,
    base_dir: str | None = None,
    subdir: str | None = None,
) -> RepoHandle

Materialize an upstream git repo (or a subtree) into the content-addressed store at a pinned commit, so its modules are importable without vendoring the files. See De-vendoring for the full pattern.

ParameterTypeDefaultDescription
urlstrrequiredRepo reference github.com/org/repo@<sha> (also https://, git://, org/repo@<sha>, or a local path). Always pin a SHA.
includelist[str] | NoneNoneRestrict materialisation to these subtrees
patcheslist[tuple[str, str, str]] | NoneNone(file, find, replace) tuples applied after checkout
diffstr | list[str] | NoneNoneUnified/git diff(s) — inline text or .patch path — applied with git apply; a hunk that doesn't apply raises
overlaystr | dict | NoneNoneNet-new / whole-file replacements: a dir path or a relpath -> bytes|str mapping
base_dirstr | NoneNoneBase dir for relative diff/overlay paths (defaults to the caller's directory)
subdirstr | NoneNoneRe-root the tree at this repo subdirectory — its packages become top-level importables; include stays repo-root relative

Apply order: checkout @shadiffoverlaypatchessubdir re-root.

The handle does three jobs:

REPO = store.repo("github.com/org/repo@<sha>", include=["pkg"])   # module top

DEPS = Dependencies(python=[*REPO.deps(), "extra==1.2"])          # upstream's own deps

@store.cache(key="model")
def load(*, device):
    with REPO:                          # scoped import — path removed after the block
        from pkg import Thing
    return build(Thing, device=device)
  • REPO.deps(extra=None, exclude=None, pin_overrides=None) → the upstream's own python dependency list, parsed at the pinned SHA from requirements.txt / pyproject.toml [project.dependencies] / setup.py install_requires. Selectors: extra="name" picks an optional-dependencies extra; exclude=["dep", ...] drops upstream deps by canonicalized name; pin_overrides={"dep": "==1.2"} replaces a dep's version spec (an overridden dep survives even if also excluded). For urun deploy's static analysis, the selector arguments must be literal constants, and a repo whose deps you splice must not carry diff=/overlay= deltas (deploy aborts loudly — declare the resulting deps as literals instead).
  • with REPO: → a context manager that puts the repo's modules on the import path only inside the block. Compile-safe: it scopes the path (no new top-level repo import resolves after the block), while modules imported inside the block stay resident for the process — a model loaded under the block still works when torch.compile traces it later.
  • REPO.sha → the resolved, pinned commit SHA.

store.get()

store.get(
    key: str | ObjectRef | ResidentRef,
    *,
    target: str | None = None,
    variant: str | None = None,
    accept: Callable[[Any], bool] | None = None,
) -> Any | None

Load content by a Store key, object id, ObjectRef, or ResidentRef set via store.set() / store.put(). For URIs (hf://, s3://, http://, git://), use store.path() or store.model() instead — store.get() does not resolve URI schemes.

ParameterTypeDefaultConstraintsDescription
keystr | ObjectRef | ResidentRefrequiredStore key, object id, ObjectRef, or ResidentRefContent identifier
targetstr | NoneNoneValid directory pathReserved; not used by get() today
variantstr | NoneNone--Select a model variant. Tries {key}:{variant} first, then falls back to the base key.
acceptCallable[[Any], bool] | NoneNoneResident-ref form onlyPayload-type predicate — a tier hit the predicate rejects is treated as a miss

Use store.get() to load previously store.set() / store.put() content. Returns None if the key is not found.

from urun import store

store.set("checkpoint", weights)
weights = store.get("checkpoint")

# Select a variant (falls back to the base key if the variant is absent)
model = store.get("my-model", variant="int8")

# Download a HuggingFace model's local path instead
path = store.path("hf://meta-llama/Llama-2-70b")

store.set()

store.set(key: str, value: Any) -> str

Store named key-value data. Overwrites any existing value for the key. Returns the key (same as the input), so a chained get doesn't need a separate variable.

ParameterTypeDefaultConstraintsDescription
keystrrequired--Name to store under
valueAnyrequiredMust be serializableContent to store

Use store.set() for named, addressable data like configs, session artifacts, and model weights.


store.put()

store.put(obj: Any) -> ObjectRef

Store an ephemeral object and get a ref-counted handle. The object is lazily evicted when no references remain.

ParameterTypeDefaultConstraintsDescription
objAnyrequiredMust be serializableObject to store

Returns: ObjectRef — a ref-counted handle to the stored object.

Use store.put() for intermediate data passed between functions. Unlike store.set(), there is no name — the ref handle is how you retrieve the data.

from urun import store

ref = store.put(large_tensor)
# Pass ref to another function
upscale(frame_ref=ref)

store.path()

store.path(key: str) -> str

Get the filesystem path for a stored key. Downloads the content if not already cached locally.

ParameterTypeDefaultConstraintsDescription
keystrrequiredURI or Store keyContent identifier

Returns: str — absolute filesystem path.

Use store.path() when you need a file path instead of loaded content (e.g., passing to libraries that expect paths).


store.lookup()

store.lookup(
    pointer_key: str,
    target: str | None = None,
) -> Any

Resolve a pointer to the latest version of stored content. Useful for "latest artifact" patterns where the pointer key stays the same but the underlying data changes.

ParameterTypeDefaultConstraintsDescription
pointer_keystrrequired--Pointer key to resolve
targetstr | NoneNoneValid directory pathLocal directory to download into

Use store.lookup() to follow a pointer to the latest version of a model or artifact.


store.stream()

store.stream(
    name: str,
    format: str | None = None,
    codec: str | None = None,
    fps: int | None = None,
) -> EventStream

Create a global stream. Global streams are addressable by name and accessible from any process. Bidirectional — both sides can emit.

ParameterTypeDefaultConstraintsDescription
namestrrequired--Stream identifier
formatstr | NoneNone--Data format hint
codecstr | NoneNone--Compression codec
fpsint | NoneNonePositive integerTarget frame rate for video streams

Returns: EventStream — a bidirectional stream handle.

Use store.stream() for global event streams (e.g., progress updates, metrics). For session-scoped streams, see Context.

from urun import store

metrics_stream = store.stream("fleet-metrics")
await metrics_stream.emit("health", {"fps": 20.0, "active_sessions": 12})

Dict-style access

store[key]              # equivalent to store.get(key)
store[key] = value      # equivalent to store.set(key, value)
del store[key]          # delete key from store
key in store            # membership test, returns bool

Dict-style access provides convenient shorthand for common store operations.

from urun import store

store["my-config"] = {"lr": 1e-4, "batch_size": 32}
config = store["my-config"]

if "my-model" in store:
    model = store["my-model"]

@store.cache()

@store.cache(
    key: str | None = None,
    prefix: str = "",
    ttl: int | None = None,
    gpu_pin: bool = True,
    process_local: bool = False,
    max_gpu_memory_mb: int | None = None,
)

Decorator that caches a loader's result across calls and sessions. On first call the function executes and the result is cached resident; subsequent calls return the cached object directly — zero-copy for GPU-pinned tensors.

ParameterTypeDefaultDescription
keystr | NoneNoneExplicit cache key. With app.store it is auto org+app-scoped ("<org>:<app>:<key>"). The canonical form for model loaders.
prefixstr""Namespace for cache keys (e.g., "model", "pipeline")
ttlint | NoneNoneTime-to-live in seconds; None = infinite
gpu_pinboolTrueKeep CUDA tensors in GPU memory (fastest, zero-copy)
process_localboolFalseSkip distributed store -- keep in worker process memory only
max_gpu_memory_mbint | NoneNoneCap GPU memory usage for this cache

The canonical loader is @store.cache(key="...") def load(*, device): return model — device comes from the platform (ctx.device), never hardcoded. Beyond memoizing the load, store.cache is the weights-residency tier (content-addressed, tiered GPU memory → host memory → local disk → peers → durable storage) and the snapshot collaborator: cached weights are excluded from the process snapshot on capture and streamed back from the resident tier on restore, so the snapshot image is weight-free. There is no lifecycle on the loader — warmup, precompile, and validation all happen in ctx.self_test.

app = App("liveavatar")
store = app.store

@store.cache(key="wan22-s2v-14b")        # resolves to "<org>:liveavatar:wan22-s2v-14b"
def load_model(*, device):
    import torch
    from diffusers import DiffusionPipeline
    return DiffusionPipeline.from_pretrained(
        store.path("hf://org/wan22-s2v-14b"), torch_dtype=torch.bfloat16
    ).to(device)

process_local=True is for non-serializable runtime objects (live pipelines, compiled graphs) that must stay in the worker process. gpu_pin=True (the default) keeps CUDA tensors on-device for zero-copy access. The older prefix=/gpu_pin= callers keep working; key= is additive and is the recommended form.

Cache control:

# Clear all entries with a prefix
load_model.clear_cache(prefix="model")

# Get cache stats
info = load_model.cache_info()

store.cache_stats()

store.cache_stats() -> dict

Get statistics about the local cache including size, hit rate, and entry count.

Returns: dict — cache statistics.

Use store.cache_stats() to monitor cache utilization and debug download behavior.


store.clear_cache()

store.clear_cache() -> None

Clear the local disk cache. Forces re-download on next store.get() call.

Use store.clear_cache() to free disk space or force fresh downloads.


store.configure_cache()

store.configure_cache(
    cache_dir: str | None = None,
    max_size_gb: float | None = None,
) -> None

Configure cache settings.

ParameterTypeDefaultConstraintsDescription
cache_dirstr | NoneNoneValid directory pathCache directory location
max_size_gbfloat | NoneNonePositive numberMaximum cache size in GB

Use store.configure_cache() to change cache location or set size limits.

On this page