Image basics
bladekit.Image is an immutable image definition: a base plus an ordered list of layers. Every builder method (apt_install, pip_install, env, run_commands) returns a new Image with one extra layer appended — the receiver is never mutated, so a shared base can safely branch into several images.
import bladekit
base = bladekit.Image.debian_slim(python_version="3.12").apt_install("git")
torch_image = base.pip_install("torch==2.3.0")
jax_image = base.pip_install("jax==0.4.28") # `base` itself is unchangedThe image= parameter accepted by @app.function, @app.cls and bladekit.app(...) takes either of two things: a plain string — a catalog alias ("fabric/torch") or a raw registry ref — passed straight through to the run request, or a **bladekit.Image** DSL value. A DSL image is not built eagerly: the first .remote() / .spawn() call on a function using it resolves the definition to a build, idempotently by definition_hash (an unchanged definition triggers zero rebuild work server-side), streaming build logs to the terminal until the build succeeds or raising bladekit.ImageBuildError on failure.
Image.from_catalog
classmethod Image.from_catalog(alias: str) -> ImagealiasstrCatalog alias, e.g.
"fabric/torch"— the same string you could pass directly asimage="fabric/torch".
Builds a base from an AI Fabric-maintained catalog image by alias. The alias is resolved server-side against the enabled catalogue at build time, so the underlying registry (and its host) never appears in your code and can move — including to an external registry — without changing anything downstream. An unknown or disabled alias fails the build with a clear error rather than silently pulling nothing.
image = bladekit.Image.from_catalog("fabric/torch").pip_install("transformers==4.42.3")
@app.function(gpu="RTX-A4500", image=image)
def embed(texts: list[str]):
...Image.from_registry
classmethod Image.from_registry(ref: str) -> ImagerefstrRegistry image reference, carrying an explicit tag or digest.
Builds a base from a public registry image on the deployment's allowlist, for cases the catalog doesn't cover. Same rule as run refs elsewhere in AI Fabric: no implicit latest — ref must contain an explicit tag (:) or digest (@), or construction raises InvalidArgumentError immediately rather than failing the build later.
image = bladekit.Image.from_registry(
"nvcr.io/nvidia/pytorch:24.10-py3"
).pip_install("accelerate==0.31.0")Image.debian_slim
classmethod Image.debian_slim(python_version: str = "3.12") -> Imagepython_versionstrDefault:"3.12"major.minorPython version, e.g."3.12".
A platform-maintained Debian slim base with the given Python preinstalled — the usual starting point for a from-scratch image built up with apt_install / pip_install layers. The exact base image digest is a server-side config, not part of the definition hash, so a platform update to the underlying digest never changes your image's definition_hash.
Layer builders
apt_install
def apt_install(self, *packages: str) -> Image*packagesstrDebian package names, appended verbatim to an
apt_installlayer — no shell, no options.
pip_install
def pip_install(
self,
*packages: str,
index_url: Optional[str] = None,
extra_index_urls: Optional[Sequence[str]] = None,
) -> Image*packagesstrPEP 508 package specifiers.
index_urlOptional[str]Default:NoneCustom PyPI index URL for this layer.
extra_index_urlsOptional[Sequence[str]]Default:NoneAdditional index URLs consulted alongside
index_url.
env
def env(self, vars: Mapping[str, str]) -> ImagevarsMapping[str, str]Non-empty mapping of environment variables baked into the layer.
run_commands
def run_commands(self, *commands: str) -> Image*commandsstrShell command lines; each one becomes its own
RUN/ cache step, in order.
| Limit | Value |
|---|---|
| Layers per image | 64 |
Packages per apt_install / pip_install layer | 256 |
Commands per run_commands layer | 64 |
Vars per env layer | 64 |
Definition, hashing & caching
An Image is, in essence, a JSON document: to_definition() returns it as a dict (version, base, layers); canonical_json() renders it as canonical bytes (sorted keys, no spaces, UTF-8); definition_hash() is the lowercase-hex sha256 of those bytes. Layer order is always preserved — it is never sorted, since it changes build semantics.
def to_definition(self, *, sandbox: bool = False) -> Dict[str, Any]
def canonical_json(self, *, sandbox: bool = False) -> str
def definition_hash(self, *, sandbox: bool = False) -> strsandboxboolDefault:FalseMark the definition as a sandbox image: the server injects
forge-sandboxdas the entrypoint instead of the usual function runner. Part of the canonical JSON, so a sandbox build never collides with a function build of the identical base/layers.
Builds are idempotent by definition_hash: the control plane deduplicates on it, so re-running the exact same definition performs zero rebuild work, and the SDK additionally caches definition_hash → build_id per App instance to skip even the lookup for an image reused across functions in one process.
Reference from the Blade console · Updated .