This page as Markdown · All pages (llms.txt)

Deploys and credits

Deploys#

A deploy is one GPU instance serving one compiled model. Each deploy starts a fresh instance, which takes a few minutes.

Python
from datetime import timedelta

minted = model.create_client(label="acme-clinic")
# hand minted.token to the client

with model.deploy(key_set=minted, max_lifetime=timedelta(hours=1)) as deploy:
    ...  # keep the deploy up while the client runs
Argument Default Range
key_set required, unless dev_mode=True The key set the deploy serves: a ClientToken, a registered ClientKeySet, or its c_id
dev_mode False True starts a developer-mode deploy; pass no key_set with it
max_lifetime required 30 minutes to 5 hours
idle_timeout 900 s 300 s to 3600 s; the gateway refuses a value outside that range
instance_type rtx-pro-6000 rtx-pro-6000 only (1x NVIDIA RTX PRO 6000 Blackwell)
timeout 600 s How long the SDK waits for the instance to become ready

A deploy stops at the first of:

  • leaving the with block, or deploy.stop()
  • max_lifetime after it started
  • provider.deploys.stop_all()

The with form stops the instance even when your code raises. If the instance does not start within timeout, model.deploy() stops it and raises belfort_ml.TimeoutError. A Ctrl-C while the instance starts stops it too; if that stop fails, it prints the deploy id and a python -c command that stops it, since the instance keeps billing. A Python process that is killed does not stop its deploy; it runs until max_lifetime unless you call provider.deploys.stop_all().

A deploy serves one key set for its whole life: the one you name in key_set. A key set holds the evaluation keys of one private key, so the deploy holds one set of weights and one set of evaluation keys, and never takes another key set. The gateway loads the key set's keys once the deploy is ready and the end client has uploaded them. If the keys are in when the deploy turns ready, model.deploy starts that load itself and returns once the GPU holds them. A load that fails there only logs a warning, since the end client's next poll tries it again. The keys stay there until the Belfort ML client ends, you unload them, or the deploy stops.

To keep a deploy running longer, deploy.extend(timedelta(minutes=30)). The total lifetime is capped at 5 hours from start.

deploy.terminates_at says when a deploy will stop. provider.deploys.list() and the console show what is running.

An account runs one deploy at a time. Since a deploy serves one key set, that is one end client at a time. While a deploy is starting or ready, model.deploy() raises belfort_ml.DeployLimitReached (a ConflictError, with .limit and .running); stop that deploy first. A deploy that is stopping does not count.

Developer mode#

Developer mode skips the generation and upload of the end client's evaluation keys, which can take minutes for a large model. Use it to try a model, not to serve one.

Python
with model.deploy(dev_mode=True, max_lifetime=timedelta(minutes=30)) as deploy:
    deploy.client_token  # for the end client, as BELFORT_CLIENT_TOKEN
    deploy.secret_seed   # 64 hex characters, as BELFORT_CLIENT_SECRET_SEED

The gateway opens a key set and chooses its secret seed, and the GPU generates the evaluation keys from it. Once the deploy is ready, it reports secret_seed and a client_token for that key set. An end client started with both resumes the key set, as in The secret key's lifetime, and starts inferring without uploading keys.

Belfort can decrypt

The gateway stores the secret seed, so Belfort can decrypt everything a developer-mode deploy serves. Never send real data to one.

To reuse the same seed on a later deploy, pass that deploy's c_id as key_set, without dev_mode; the deploy runs in developer mode because its key set does. The key set and its tokens expire 24 hours after the first deploy, and the gateway deletes the seed when the key set expires or the end client releases it. Passing both key_set and dev_mode=True, or neither, raises ValueError.

Running continuously#

A deploy lives at most 5 hours. Serving an end client around the clock means starting a new deploy for its key set when the last one ends:

serve.py
import time
from datetime import timedelta

while True:
    with model.deploy(key_set=minted, max_lifetime=timedelta(hours=5)) as deploy:
        while deploy.refresh().status == "ready":
            time.sleep(60)

What happens to the end client across deploys:

  • Its key set outlives the deploy. When the end client next polls its status, the gateway loads the key set onto the next ready deploy that serves it.
  • An infer() that finds no deploy holding its keys waits for the next deploy and sends the same ciphertext again, if the first attempt cannot have run. Otherwise it reports the outcome as unknown.
  • The token must outlive the deploys: mint it with a ttl_seconds long enough. When it expires, the gateway deletes the key set's keys.
  • A long-running Client needs no new token until its token expires, after 7 days at most. belfort-ml-client infer needs a new token for every run unless every run sets the same BELFORT_CLIENT_SECRET_SEED.

The end client cannot run inferences while the next deploy starts, which takes a few minutes, and while its keys load. An account runs one deploy at a time, so the next deploy starts only once the current one is stopping. A 5-hour deploy reserves 30 credits, three times the grant of a new account, so continuous serving needs more credits than the grant.

Credits#

Credits pay for deploy time. Compiling, uploading keys and running inferences are free.

1 credit 10 minutes of deploy time, rounded up
New account 10 credits (100 minutes)
More credits Request more credits on the console's Credits page

When a deploy starts, ceil(max_lifetime / 10 min) credits are reserved, so the shortest deploy, 30 minutes, reserves 3; if your balance is too low, model.deploy raises InsufficientCredits (with .required and .available) and nothing starts. When it stops, you are charged for the time it ran from becoming ready, rounded up to 10 minutes, and the rest is released; a deploy that fails to start costs nothing. extend reserves the difference. Example: max_lifetime=timedelta(hours=1) reserves 6 credits; stopped after 25 minutes, it costs 3. provider.credits.get()["credit_minutes"] gives the length of a credit.

Python
provider.credits.get()           # {"granted": 10, "used": 3, "reserved": 6, "available": 1, "credit_minutes": 10}
provider.credits.transactions()  # one page, newest first