The class
Two methods are required. Three more are optional, discovered by name; no base class, no decorator, no platform import.
scripts/my_serve.py
class YourPredictor: # any name — the yaml points at it
def __init__(self, **init_args): # REQUIRED
# init_args come verbatim from inference.yaml → model.init_args.
# Build the model, pick a device, load tokenizers. Runs once per replica.
...
def predict(self, data: bytes) -> dict: # REQUIRED — may be async def
# `data` is the normalized request body. Return a JSON-serializable dict.
...
def load_weights(self, path: str) -> None: # optional
# Called after __init__ IF the yaml sets model.path. `path` is the
# local file the platform mounted for you.
...
def warmup(self) -> None: # optional
# Hit once per replica after deploy — pre-warm JIT / autotune caches.
...
def health(self) -> dict: # optional
# Returned verbatim on GET /health. Absent → {"status": "ok"}.
...The smallest complete predictor is five lines:
Python
class Echo:
def __init__(self, prefix: str = "echo: "):
self.prefix = prefix
def predict(self, data: bytes) -> dict:
return {"echo": self.prefix + data.decode("utf-8", errors="replace")}What `data` is
The platform normalizes every request body into raw bytes before your method sees it, keyed on the request's Content-Type:
warn
application/json is an envelope, not a passthrough: any field other than url / data_b64 is rejected. To send structured JSON (payload plus parameters in one request), POST the JSON bytes as application/octet-stream and call json.loads(data) in your predictor. The SDK's predict_json() does exactly this; details in Calling your model.A robust predictor tries json.loads first and falls back to treating the bytes as the raw payload. The SAM 2.1 example does this: JSON with prompts, or a bare image.
Returns and errors
- Return only JSON-serializable values: ints, floats, strings, lists, dicts. Tensors and numpy scalars cause a 500; convert them. Base64-encode binary outputs (the SAM example returns masks as base64 PNGs).
- Raise
ValueErrorfor bad input; the platform maps it to a 400 with a clean error body. Any other exception becomes a 500. Never return an error dict with a 200 status; raise instead. - Each request may run for 60 seconds; after that the caller gets 504. A client can't extend it. For slow models (diffusion, long TTS), split the work into shorter requests or contact Support to raise the limit.
note
Concurrency: up to
cluster.max_ongoing_requests calls can be inside your predict at once. Either make it thread-safe (model in inference mode, no shared mutable state) or keep the cap low. Exceptions in __init__ or load_weights crash-loop the replica, so fail fast and loudly there.