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

Provider

Configuration#

Python
import belfort_ml

provider = belfort_ml.Provider()                        # from the environment
provider = belfort_ml.Provider(api_key="blf_live_...")  # or explicit
Argument Environment Default
api_key BELFORT_API_KEY required
base_url BELFORT_BASE_URL https://eml.belfortlabs.com/api/v1
timeouts Timeouts(): 30 s standard, 300 s per upload part, 420 s inference, 10 s poll
max_retries 3

Explicit arguments take precedence over the environment. A missing key raises ConfigurationError when the provider is constructed.

Set BELFORT_BASE_URL only to point at a different gateway, for example http://localhost:8000/api/v1 for a local one. For the gateway serving these docs:

Terminal
export BELFORT_BASE_URL=https://eml.belfortlabs.com/api/v1

The provider holds a connection pool. Use it as a context manager or call close():

Python
with belfort_ml.Provider() as provider:
    ...

Objects#

provider.account.get() and provider.credits.get() return plain dicts. provider.models and provider.deploys return objects that carry their own methods, so ids rarely need to be passed around.

Object Returned by Key attributes
Model provider.models.create/get/list id, status, params, active_compile_id, level_zero_encryption, use_composite_relu
ClientKeySet model.clients id, label, status, size_bytes
Deploy model.deploy, provider.deploys.get/list id, c_id, status, terminates_at, credits_reserved; secret_seed and client_token once a developer-mode deploy is ready
LoadedKeys deploy.load_keys k_id
ClientToken model.create_client token, c_id, m_id, label, expires_at
InferenceResult deploy.infer output, request_id, latency_ms

Methods#

Method What it does
belfort_ml.compile(model, example_input, name, output_dir, calibration_inputs=, allow_unsupported_ops=False) Local compile to MLIR; returns Artifacts. name must be a C++ identifier that is not a keyword. Raises CompileError on an operation the Belfort compiler does not support; allow_unsupported_ops=True writes the MLIR and logs a warning instead. Warns with MultiCiphertextWarning when a layer does not fit one ciphertext
provider.account.get() Your account, including mp_id and credits
provider.credits.get() Credit balance
provider.credits.transactions(cursor=, limit=, kind=) One page of credit transactions; follow next_cursor
provider.models.create(artifacts, name=, wait=True, timeout=3600, level_zero_encryption=False, use_composite_relu=True) Register, upload, compile, wait. See Level-zero encryption and ReLU approximation
provider.models.get(m_id) / provider.models.get(name=) / provider.models.list() Look up models; list follows pages lazily
provider.models.get(name=, create_from=artifacts, timeout=3600, level_zero_encryption=False, use_composite_relu=True) Return the model of that name, creating it from artifacts when missing and waiting until it is ready. A model of that name with a different MLIR, level_zero_encryption or use_composite_relu raises ConflictError name_taken
model.refresh() / model.wait_until_ready() Re-read the compile status
model.delete() Delete a model with no in-use deploy, with its uploaded MLIR, its key sets and its stopped deploys
model.create_client(label, ttl_seconds=) Open a key set for one end client and mint the token it authenticates to the gateway with, valid 24 hours by default
model.wait_for_keys(key_set, timeout=3600) Wait until the end client has uploaded its evaluation keys and the gateway verified them. Raises KeyMaterialError on a failed or expired key set
model.clients(status=) The model's key sets, with id (the c_id), label, status and size_bytes
model.delete_client(c_id) Delete a key set that no running deploy serves, which ends its client token
model.deploy(key_set=, max_lifetime=, timeout=, dev_mode=False) Start a GPU deploy that serves key_set (a ClientToken, a ClientKeySet or a c_id) for its whole life, and wait for it. dev_mode=True, without key_set, is for testing: see Developer mode
provider.deploys.get(d_id) / .list() / .stop_all() Look up or stop deploys
deploy.load_keys() / deploy.unload_keys(loaded) Put the deploy's key set on the GPU, or remove it
deploy.infer(loaded, ciphertext) / deploy.infer_batch(loaded, [...]) Run an inference on a ciphertext you hold
deploy.extend(additional) / deploy.stop() / deploy.refresh() Manage the deploy

deploy.infer is for ciphertexts that are already in your hands, for example test data an end client sent you. Normally the end client calls the gateway itself with the token from model.create_client, so the ciphertext does not pass through you, and the gateway loads its keys onto the deploy without a load_keys call. load_keys is for loading a key set onto a deploy yourself.

Every method that waits (create, get(name=, create_from=), deploy, load_keys) polls the resource's status, starting every 2 seconds and backing off to 10, and raises TimeoutError when its timeout runs out. wait=False returns immediately where it is offered. While a deploy starts, deploy writes the deploy id and each launch step (looking for a GPU instance, booting it, loading the model, then loading the client's keys if the end client has uploaded them) to stderr; progress=False turns that off.

Level-zero encryption#

level_zero_encryption=True compiles the model so that the end client encrypts each input at the bottom of the modulus chain, and the deploy bootstraps each input before the computation.

This is a tradeoff: it will make the ciphertext at the client much smaller, hence reducing upload time and bandwidth usage. The computation will however take longer as an additional bootstrap operation will be performed at the beginning. It is not recommended be use it with models that do not require bootstrapping as it will greatly increase the evaluation keys' size.

ReLU approximation#

Encrypted computation cannot evaluate ReLU exactly, so the compiler replaces each ReLU with a polynomial over the range that calibration recorded.

With use_composite_relu=True, the default, each ReLU is a composite approximation: three chained polynomials, of degrees 15, 15 and 27. It is accurate, but it uses many levels per activation, so the model bootstraps more often. More levels and bootstraps make each inference slower and the evaluation keys larger.

use_composite_relu=False uses a single degree-5 polynomial instead. A small model can then fit without bootstrapping, with much smaller keys. The approximation is coarser, so compare the encrypted results with the plaintext model before you use it. The Quickstart uses it.

These two are the only choices: the degree of the approximation, for ReLU and for the other approximated operations, cannot be set yet.

Uploads#

provider.models.create uploads the MLIR, and the Python file that defines the model when artifacts.source_path is set, to storage through presigned URLs, in parts, without sending your API key. A failed part is retried; an upload that still fails raises UploadError.

Logging#

The SDK logs to the standard belfort_ml logger, which is silent unless you configure a handler. DEBUG lines contain the method, path, status and request id, and omit headers and bodies.

Python
import logging
logging.basicConfig(level=logging.INFO)