docs
Python SDK

Dependencies

Tell urun what your function needs -- Python packages, system libraries, and post-install steps

Tell urun what your function needs — Python packages, system libraries, and post-install steps.

Your GPU functions run in isolated environments. Dependencies tells urun what to install before your function starts. Import it from urun.core:

from urun.core import Dependencies

Add PyTorch

deps = Dependencies(python=["torch>=2.5.0"])

@app.function(gpus="h100:1", deps=deps)
def serve(ctx):
    import torch
    ...

Add pip packages

List everything your function imports:

deps = Dependencies(python=["torch", "transformers", "accelerate"])

@app.function(gpus="h100:1", deps=deps)
def serve(ctx):
    from transformers import AutoModelForCausalLM
    ...

Version pins work the same as pip:

deps = Dependencies(python=[
    "torch>=2.5.0",
    "transformers==4.40.0",
    "accelerate>=0.28",
])

Add system libraries

Some Python packages depend on system-level libraries (ffmpeg for video, OpenGL for rendering). Use apt= to install them:

deps = Dependencies(
    python=["torch", "opencv-python"],
    apt=["ffmpeg", "libgl1-mesa-glx"],
)

@app.function(gpus="h100:1", deps=deps)
def process_video(video_path: str):
    import cv2
    ...

Build Flash Attention from source

Packages that require custom build steps (no pre-built wheels, need --no-build-isolation, etc.) go in post_install:

deps = Dependencies(
    python=["torch>=2.5.0"],
    post_install=["pip install flash-attn --no-build-isolation"],
)

@app.function(gpus="h100:1", deps=deps)
def serve(ctx):
    from flash_attn import flash_attn_func
    ...

post_install commands run after all python and apt dependencies are installed, so build dependencies are available.

Ship a data file with your code

urun deploy collects your app by following Python imports from the entrypoint, and ships the non-Python files sitting next to that source. Anything it does not reach — a prompt tensor in its own directory, a fixture an .urunignore pattern excludes — you declare with files=:

deps = Dependencies(
    python=["torch"],
    files=["flashvsr_utils/prompt_tensor/posi_prompt.pth"],
)

@app.function(gpus="h100:1", deps=deps)
def superres(ctx):
    ...
  • Paths are relative to your entrypoint file (app.py), and land at the same relative location in the pod.
  • Declared files ship regardless of .urunignore and the built-in exclusions. That is what declaring them is for — you never have to rename a file to get it past a pattern.
  • files= is purely additive: with no files=, collection is exactly what it was.
  • Entries are individual files. Directories and globs are not accepted — list each file, so what ships stays explicit and reviewable.
  • A declared path that does not exist, is absolute, or points outside your app directory fails the deploy immediately, naming the path and the directory it was looked up under. A file you declare is never silently skipped.

Large model weights do not belong here — fetch those through the store so they are cached on the cluster instead of re-uploaded with every deploy. files= is for the small assets that are part of your source tree.

Pin the Python version

deps = Dependencies(
    python_version="3.11",
    python=["torch", "transformers"],
)

If not specified, urun chooses the version. Set python_version when a package requires a specific one.

Attaching dependencies to functions

Dependencies attach to functions through the deps= parameter on @app.function:

from urun import App
from urun.core import Dependencies

app = App("my-app")

video_deps = Dependencies(
    python=["torch>=2.5.0", "diffusers", "transformers"],
    apt=["ffmpeg"],
    post_install=["pip install flash-attn --no-build-isolation"],
)

@app.function(gpus="h100:1", deps=video_deps)
def generate(ctx):
    ...

Different functions can have different dependencies — a lightweight voice function might need far fewer packages than a video pipeline:

voice_deps = Dependencies(python=["torch>=2.5.0", "transformers"])

@app.function(gpus="l4:1", deps=voice_deps)
def voice(ctx):
    ...

Common presets

urun includes presets for common inference stacks (LLM serving, video generation, voice). These bundle the typical packages you need so you don't have to list them individually. Check urun.core for available options (ML_TRAINING_DEPS, ML_INFERENCE_DEPS, VIDEO_PROCESSING_DEPS, HUGGINGFACE_DEPS, DISTRIBUTED_DEPS).

Inheriting an upstream repo's dependencies

When your function imports an upstream model repo, don't hand-copy its requirements.txt. Declare the repo with store.repo and splice its own dependency list in:

store = app.store
REPO = store.repo("github.com/org/model-repo@<sha>", include=["model"])

deps = Dependencies(python=[*REPO.deps(), "torch>=2.5.0"])

REPO.deps() parses the upstream's dependencies at the pinned commit, so you can't drift out of sync with a copy. Shape the splice with extra="name" (an optional-dependencies extra), exclude=["dep", ...] (drop upstream deps by name), and pin_overrides={"dep": "==1.2"} (replace a version spec). See De-vendoring for importing the upstream code itself and the static-analysis rules the selectors follow.


For secrets and API keys (HuggingFace tokens, external API keys), see Credentials.

Related:

  • De-vendoring — Pull in upstream model code with store.repo
  • Credentials — Passing secrets to your GPU functions
  • App — The @app.function decorator

On this page