Skip to content

Being operable

Two audiences, and they want different things.

KAI-C reads /capabilities

Identity, model info and fingerprint, the tasks advertised, permissions, scheduling and cost. It re-reads every 60 seconds, and a changed model.fingerprint is what drift detection keys on.

Declare permissions honestly: KAI-C refuses to register an adapter asking for more than the operator granted, so over-declaring blocks the deployment and under-declaring means the access you need is not there.

The operator reads /metrics

The SDK already exports the §3.4 baseline — in-flight calls, inference latency and outcome per task, a model-loaded gauge, and adapter_model_info identity labels carrying the fingerprint.

Register your own for what only the model knows:

    metrics.register_counter(
        "adapter_frames_skipped_total",
        "Frames dropped before inference because they were too dark.")
    metrics.register_histogram(
        "adapter_preprocess_seconds",
        "Time spent resizing and normalising, excluding the model.",
        buckets=(0.001, 0.005, 0.01, 0.05, 0.1),
    )
    return object()


@adapter.on_image()

A counter an operator can alert on beats a log line nobody reads: "this camera has been sending unusable frames since 3am" is a metric, not a logger.warning.

Health has to be honest

/health must go red when the model did not load. A green dot on a dead adapter routes real work into a hole, and the operator has no way to tell. The facade derives this from the loader, so the only way to get it wrong is to swallow the exception yourself.

is_ready() is a bool, so on its own it cannot tell "still loading" from "the load failed"/health would say loading forever, and both Docker's healthcheck and opennvr-adapter validate would pass a dead adapter. If you implement AdapterService directly rather than through the facade, override health_status() and return the real HealthStatus:

def health_status(self) -> HealthStatus | None:
    return self._state      # OK / DEGRADED / LOADING / ERROR

Returning None keeps the old bool-derived behaviour. The conformance runner FAILs on error and WARNs on loading, so neither goes green.

Weights: baked in or fetched

Baked into the image Fetched on first load
Reproducible yes only if the URL is pinned
Air-gapped site works needs the volume pre-populated
Image size large small
Fingerprint fixed at build fixed after first boot

ensure_model_file handles both: a file already present always wins and nothing is downloaded, so the same image works in a deployment running sovereignty=local_only where the operator pre-populated the volume.

Full examples: 06_capabilities_and_metrics.py, 08_weights_and_packaging.py.