Developer docs / AI Fabric SDK / Core API

App & decorators

Construct a bladekit.app and register remote work with @app.function, @app.cls, @app.method and the @bladekit.enter() lifecycle hook.

bladekit.app

An AI Fabric application: a named namespace of remotely-callable functions and classes. Calling bladekit.app() registers nothing on its own — the @app.function and @app.cls decorators populate it, and blade run / blade deploy load the module, read app.functions / app.classes, and ship them.

Signature
bladekit.app(name: str, *, image: Any = None)

name str

Application name — the deployment identity used by the CLI and the control plane.

image str | bladekit.ImageDefault: None

Default image for every function that does not set its own image=. A catalogue alias / registry ref (string) or a bladekit.Image DSL object.

Construct an app
import bladekit

app = bladekit.app("demo", image="fabric/base")

@app.function

Decorator that registers a plain function for remote execution, returning a Function handle exposing .remote(), .spawn() and .map(). Usable bare (@app.function) or with arguments (@app.function(gpu="RTX-A4500")).

Signature
@app.function(
    *,
    gpu: str | None = None,
    cpu: float | None = None,
    memory: int | None = None,
    timeout: int | None = None,
    image: Any = None,
    idle_timeout: int | None = None,
    volumes: dict[str, bladekit.Volume] | None = None,
    secrets: list[bladekit.Secret] | None = None,
    retries: int | dict | None = None,
    priority: int | None = None,
    namespace: str | None = None,
    schedule: bladekit.Cron | None = None,
    gpu_class: str = "spot",
    best_effort: bool = False,
    max_interruptions: int | None = None,
)

gpu strDefault: None

GPU type, e.g. "RTX-A4500". Append ":N" (N between 1 and 8) for multiple GPUs in one container, e.g. "RTX-A4500:4". Omit for CPU-only.

cpu floatDefault: None

Fractional CPU cores. Minimum 0.1 (100 millicores); server default 1.0 when unset.

memory intDefault: None

Memory in MB. Server default 16384 (16 GB) when unset.

timeout intDefault: None

Max seconds for one execution — it bounds the work, never the worker. A one-shot blade run / cron / map run is killed past it; on a deployed endpoint it caps each request, and the service itself never expires (replica lifetime belongs to the autoscaler: min_replicas, idle scale-down, redeploy). Server default 3600 (1 h) when unset.

image str | bladekit.ImageDefault: None

Container image (catalogue alias, registry ref, or bladekit.Image DSL). Falls back to the App default when unset.

idle_timeout intDefault: None

Seconds a warm container lingers with no work before scale-down. Server default 60 when unset.

volumes dict[str, bladekit.Volume]Default: None

Maps a container mount path to a bladekit.Volume.

secrets list[bladekit.Secret]Default: None

Secrets injected as environment variables inside the container.

retries int | dictDefault: None

Retry policy. An int shorthand (retries=3, max 10) or a dict, e.g. {"max_retries": 3, "backoff": {"initial_seconds": 1, "max_seconds": 60, "factor": 2.0}}. Applied per-input for map().

priority intDefault: None

Scheduling priority 0–9 (9 = urgent, 0 = best-effort). Treated as 5 (FIFO) when unset.

namespace strDefault: None

Team namespace used for GPU quota accounting.

schedule bladekit.CronDefault: None

A bladekit.Cron(...) object for cron-scheduled execution. Takes effect only in a deployed app, not in blade run.

gpu_class strDefault: "spot"

Capacity class: "spot" (interruptible, cheap) or "reserved" (non-interruptible, no preemption). Validated client-side.

best_effort boolDefault: False

Opt into best-effort interruptible scheduling.

max_interruptions intDefault: None

Preemption budget: the maximum number of interruptions tolerated before the run gives up.

A GPU function
@app.function(gpu="RTX-A4500", timeout=600, retries=2)
def render(prompt: str) -> bytes:
    ...

render.remote("a red bicycle")

@app.cls

Decorator that registers a class whose @app.methods are callable remotely. This is the standard inference pattern: load a model once per container in an @bladekit.enter() hook, then reuse it across every method call. The runner instantiates the class with no arguments, runs the enter hooks, then calls the target method.

Signature
@app.cls(
    *,
    gpu: str | None = None,
    cpu: float | None = None,
    memory: int | None = None,
    timeout: int | None = None,
    image: Any = None,
    idle_timeout: int | None = None,
    volumes: dict[str, bladekit.Volume] | None = None,
    secrets: list[bladekit.Secret] | None = None,
    gpu_class: str = "spot",
    best_effort: bool = False,
    max_interruptions: int | None = None,
)

gpu strDefault: None

GPU type, with an optional ":N" multi-GPU suffix — same semantics as @app.function.

cpu floatDefault: None

Fractional CPU cores (minimum 0.1).

memory intDefault: None

Memory in MB.

timeout intDefault: None

Max seconds for one execution — same semantics as @app.function: bounds each call (or each request on a deployed endpoint), never the lifetime of a deployed service.

image str | bladekit.ImageDefault: None

Container image; falls back to the App default when unset.

idle_timeout intDefault: None

Idle seconds before a warm container scales down.

volumes dict[str, bladekit.Volume]Default: None

Mount path → bladekit.Volume map, shared by every method.

secrets list[bladekit.Secret]Default: None

Secrets injected into the container for every method.

gpu_class strDefault: "spot"

"spot" (interruptible) or "reserved" (non-interruptible) — forwarded to each per-method function.

best_effort boolDefault: False

Opt into best-effort interruptible scheduling.

max_interruptions intDefault: None

Preemption budget forwarded to each method.

A class with enter + method, deployed
import bladekit

app = bladekit.app("embeddings")

@app.cls(gpu="RTX-A4500", image="fabric/torch")
class Embedder:
    @bladekit.enter()
    def load(self):
        # runs once per container, before the first call
        from sentence_transformers import SentenceTransformer
        self.model = SentenceTransformer("all-MiniLM-L6-v2")

    @app.method()
    def embed(self, texts: list[str]):
        return self.model.encode(texts).tolist()

# local: Embedder().embed(...) ; remote:
Embedder().embed.remote(["hello", "world"])

Terminal
blade deploy embedder.py

@app.method

Decorator that marks a method of an @app.cls class as remotely callable. Access it on an instance to get a bound handle: Embedder().embed exposes .remote(), .spawn() and .local() (a direct in-process call). It takes no arguments — resources come from the enclosing @app.cls.

Signature
@app.method()
def embed(self, ...):
    ...

@bladekit.enter()

Decorator that marks a method to run once per container, before the first call — the place to load a model or open a connection. The runner instantiates the class with no arguments, runs every @bladekit.enter()-marked method, then dispatches calls. The instance and its enter side-effects are reused for every subsequent call on that warm container.

Signature
@bladekit.enter()
def load(self):
    ...

@app.local_entrypoint

Decorator that marks the function blade run invokes locally — a driver that runs on your machine and fans work out to the cluster via .remote() / .map(). Usable bare (@app.local_entrypoint) or called (@app.local_entrypoint()). Registered entrypoints are collected on app.local_entrypoints.

Signature
@app.local_entrypoint()
def main():
    ...

Fan out from a driver
@app.local_entrypoint()
def main():
    for out in render.map(["a cat", "a dog", "a bird"]):
        print(len(out))

Terminal
blade run app.py

Reference from the Blade console · Updated .

Contact the team

Loading the form…