App
API reference for App container and function registration
API reference for urun.App and function registration.
Public API at a Glance
| Name | Description |
|---|---|
App(name) | Create an app container |
@app.function() | Register a remote GPU function |
App.ref(name) | Transparent cross-app calls (refs, never strings) |
fn.copy() | Copy function with independent config |
@urun.app(name) | Class-based app decorator |
@urun.function() | Class-based function decorator |
For patterns and narrative explanation, see the App concept page.
App
from urun import App
App(name: str)Create an app container. The name identifies your app in logs, the console, and deployment.
| Parameter | Type | Default | Constraints | Description |
|---|---|---|---|---|
name | str | required | Must be unique within your account | Unique identifier for the app |
Use App as the entry point for all urun programs.
@app.function()
@app.function(
*,
gpus: str | int | None = None,
cpus: str | int | None = None,
memory: int | None = None,
deps: Dependencies | None = None,
credentials: Credentials | None = None,
env: dict[str, str] | None = None,
lease: int | None = None,
max_session_s: int | None = None,
session_connect_timeout: int | None = None,
session_disconnect_grace: int | None = None,
session_idle_timeout: int | None = None,
replicas: int | None = None,
warm: int | None = None,
max_concurrency: int | None = None,
sessions_per_pod: int | None = None,
sessions_per_user: int | None = None,
tier: str | None = None,
snapshot: bool | None = None,
expandable_segments: bool = False,
gpu_pack: Any = None, # str label | fn instance | App.ref("app").fn
gpu_pack_sm_partition: dict | None = None,
gpu_pack_limits: dict | None = None,
session_kind: str = "realtime",
) -> CallableRegister a function to run on remote GPUs. This is the primary decorator for defining GPU workloads. The kwargs are the function's config, fixed at deploy time — they are never overridable from a session invocation. Invalid values fail loudly at decoration time (app import), never silently at deploy.
| Parameter | Type | Default | Description |
|---|---|---|---|
gpus | str | int | None | None | GPU spec: "type:count" ("h100:8") or int (default GPU type). Omit for CPU-only. |
cpus | str | int | None | None | CPU spec: "per_node:total" ("8:128") or int |
memory | int | None | None | Host-memory limit in GiB (None = the per-shape default bound) |
deps | Dependencies | None | None | Python and system dependencies |
credentials | Credentials | None | None | Secrets to inject at runtime |
env | dict[str, str] | None | None | Environment variables — external inputs only, never app config |
lease | int | None | None | Renewable session length in seconds, auto-renewed while the client is connected |
max_session_s | int | None | None | Hard, non-renewable session cap in seconds. Renewal is refused past the create-time deadline; the session ends with terminal reason max_session_age. None = uncapped. Must be an int > 0. |
session_connect_timeout | int | None | None | Time allowed to establish the session transport (seconds) |
session_disconnect_grace | int | None | None | Grace period before teardown after a disconnect (seconds) |
session_idle_timeout | int | None | None | Attached-but-inert idle timeout. None = platform default (300s); 0 = opt out. The user is warned ~90s before the deadline; past it the session ends with reason idle_timeout. Clients that never report activity are exempt. |
replicas | int | None | None | Number of independent serving replicas |
warm | int | None | None | Idle replica floor: 0 = scale to zero when idle, N = keep N resident, None = legacy always-on. Cannot exceed replicas. On a GPU function (multi-rank included), declaring warm defaults snapshot=True so wakes restore instead of cold-booting. |
max_concurrency | int | None | None | Max concurrent sessions for this function (demand-proportional scale-up). None = the platform default (2, owned by the control plane). Must be an int >= 1. |
sessions_per_pod | int | None | None | Sessions one pod serves concurrently. None = platform default (1). Must be an int >= 1. |
sessions_per_user | int | None | None | Live sessions one user may hold for this function. None = platform default (1); a same-user dial reconnects to the existing session. Must be an int >= 1. |
tier | str | None | None | Declared strictness tier: "relaxed", "strict", or "packable". An inert signal today — persisted and projected, but no scheduling/admission/pricing behavior keys off it yet. |
snapshot | bool | None | None | Per-function snapshot opt-in/out. None follows the deployment gate; False skips all snapshot machinery; True opts in explicitly. |
expandable_segments | bool | False | PyTorch CUDA allocator policy. False (platform default) = fixed/static pool: stable addresses (the precondition for your own torch.compile / CUDA-graph capture) and a snapshot-capturable pool. True opts into expandable segments for fragmentation-prone apps, trading graph-capturability + snapshot. |
gpu_pack | str | fn | App.ref(...).fn | None | Same-GPU colocation. Packed functions land on the same physical GPU (same-node hard guarantee) and hand tensors across via zero-copy CUDA IPC. Three forms: a str group label (within-app), a decorated function instance of this app, or an App.ref("other-app").fn attribute (cross-app — refs, never strings). Incompatible with expandable_segments=True (loud error). |
gpu_pack_sm_partition | dict | None | None | Green-context SM carve-out ({"sm_count": N} or {"sm_fraction": f}); requires gpu_pack |
gpu_pack_limits | dict | None | None | Combined SM+VRAM+mode ladder ({"sm_fraction": .5, "vram_fraction": .5, "mode": "lightweight"}); requires gpu_pack |
session_kind | str | "realtime" | Declared session media shape: "realtime" (default) or "doc". "doc" declares a docs+artifacts app with zero media lanes — requests/results ride Yjs docs, bulk bytes ride the session artifact API — so the platform skips RTP ingest/recording wiring structurally. |
Retired kwargs — loud TypeError
timeout, topology, gpus_per_replica, colocate_on_node, event_name, scaling, and gang are retired and raise a TypeError at decoration time. The platform schedules and colocates; the model owns its own parallelism; session length is the renewable lease (with max_session_s as the hard cap); idle residency is warm. Cross-app calls via App.ref are best-effort by definition — there is no declaration surface for cross-app callees.
Use @app.function for live streaming sessions and one-shot GPU tasks. For streaming, use ctx.stream() inside the function body to create session-scoped streams and build pipelines.
@app.function(gpus="h100:1", lease=3600)
def restyle(ctx):
...
# Streaming pipeline example
@app.function(gpus="b200:2", deps=DEPS, credentials=CREDS)
async def stream_video(ctx: urun.Context):
control = ctx.doc("control")
video = ctx.stream("video", codec="h264", bitrate=8_000_000, fps=24, rank=1)
def denoise(*, stop_event, config):
while not stop_event.is_set():
yield model.step(control.desired.prompt.value) # live inline read
pipeline = (
ctx.source(denoise, device="cuda", rank=0)
| ctx.pipe(vae_decode, device="cuda", rank=1)
| ctx.unbatch(rank=1)
| ctx.pace(fps=24, rank=1)
| video
)
await pipeline.run()There is no topology kwarg. You declare a shape (gpus="b200:4"); uRun launches one rank per GPU and the model owns its own parallelism (ctx.rank / ctx.world_size inside the function). Reactive control is inline doc.desired.* reads plus doc.bind for structural change — the old pipeline.bind(doc, mapping) field-mapping is retired.
App.ref() — cross-app calls
App.ref(name: str) -> AppRefA by-name reference to another app registered by the same org. Attribute dispatch is the API: calling a function attribute on the returned ref mints a real session on that app's function and returns the session primitive (session.doc(...) / session.stream(...) — the Python equivalent of the TS client). Awaiting the call gives the one-shot result.
from urun import App, CalleeUnavailable
ref = App.ref("qwen-image-edit")
try:
session = ref.edit(prompt=prompt, frame=frame_ptr) # its own session
edited = await ref.edit(prompt=prompt, frame=frame_ptr) # one-shot
except CalleeUnavailable:
... # the callee may be scaled-to-zero, mid-rollout, or never deployed| Parameter | Type | Default | Constraints | Description |
|---|---|---|---|---|
name | str | required | An app name in your org | Target app |
Returns a distinct proxy type (AppRef), not an App — function names resolve via attribute lookup, and an unknown function fails loudly at call time with the org-scoped lookup error. The same-app form is the plain attribute: session = app.other_fn(...). Refs — never strings — cross app boundaries (they are also the cross-app form accepted by gpu_pack=). There is no ctx.call.
Best-effort by definition
App.ref cross-app calls are best-effort — there is no declaration surface for cross-app callees. The callee may be scaled-to-zero, mid-rollout, or never deployed; catch CalleeUnavailable (exported from urun) around calls that must handle that case.
fn.copy()
fn.copy(**overrides) -> UrunFunctionCreate an independent copy of a registered function, optionally overriding config. Changes to the copy do not affect the original.
| Parameter | Type | Default | Constraints | Description |
|---|---|---|---|---|
**overrides | -- | -- | Same kwargs as @app.function | Forwarded to .to(); e.g. train.copy(gpus=8) |
Use .copy() when you need multiple variants of the same function with different compute configurations.
@urun.app()
@urun.app(name: str) -> Callable[[type], type]Class-based app decorator. Turns a class into an app container where methods become registered functions.
| Parameter | Type | Default | Constraints | Description |
|---|---|---|---|---|
name | str | required | Must be unique within your account | App identifier |
Use @urun.app when you have multiple related functions that benefit from class-based organization.
@urun.app("my-studio")
class MyStudio:
@urun.function(gpus="h100:1")
def generate(self, ctx, prompt: str = ""):
...@urun.function()
@urun.function(gpus=..., deps=..., credentials=..., ...) -> CallableClass-based function decorator. Used inside @urun.app classes to register methods as remote GPU functions. Accepts the same parameters as @app.function(), including the same retired-kwarg errors.
Use @urun.function inside class-based apps. For standalone apps, use @app.function().
Runtime configuration boundary
This is the single canonical statement of what is fixed vs what can change, referenced from every "argument purity" callout in the docs.
Fixed at deploy time (config): gpus, cpus, memory, replicas, warm, max_concurrency, sessions_per_pod, sessions_per_user, deps, credentials, env, lease, max_session_s, session_connect_timeout, session_disconnect_grace, session_idle_timeout, tier, snapshot, expandable_segments, gpu_pack, gpu_pack_sm_partition, gpu_pack_limits, session_kind — everything the decorator declares. These are captured by @app.function and registered with the control plane at urun deploy.
A browser/session invocation cannot change a function's shape. Calling app.fn(args) (TS) or session = app.fn(...) / App.ref("app").fn(...) delivers args 1:1 to the Python function's own parameters and verbatim — reserved-looking names like gpus are passed through untouched, never reinterpreted as resource overrides.
The GPU shape is declared once on @app.function and fixed at deploy time. Nothing changes the shape of an already-running session: once a session is allocated, its GPU shape is fixed for the life of that session.