Skip to content

Concepts

An app asks for a task, never for your adapter

An app's manifest says requires_tasks: [license_plate_recognition]. It does not name an adapter, and it cannot. Any adapter advertising that task satisfies it — which is what makes a better plate reader a drop-in for the shipped one, swappable by the operator without touching a single app.

The consequence runs both ways, and it is the single most important thing to get right:

You do What happens
Advertise an existing convention (server/config/tasks.yml) Every app wanting that capability can use your model on day one
Invent a task name Nothing asks for it. Your adapter installs, reports healthy, and receives no work — silently

opennvr-adapter validate and the index validator both check this, because the failure has no other symptom.

The six endpoints

Read by For
GET /health KAI-C, the orchestrator May this adapter take traffic? Unauthenticated, so a failing adapter can still be scraped.
GET /capabilities KAI-C What it is, what it can do, what it needs. Re-read every 60s.
GET /hardware/evaluation the operator Can this host run it well? Rendered verbatim, so make the reasoning actionable.
GET /metrics Prometheus Whether the model is keeping up.
POST /infer KAI-C One inference.
WS /infer/stream KAI-C Many, down one warm session. Optional.

Plus two the SDK adds: GET /openapi.json and GET /asyncapi.json.

The fingerprint is a safety feature

model.fingerprint is a content hash of the weights. KAI-C records it at registration and on every poll, and a change is a tamper signal that raises an audit event.

A null fingerprint is skipped, so an adapter without one is silently exempt from the check that protects the operator. Adapter(weights=...) hashes the file for you; without a weights file the facade still derives a deterministic value rather than returning nothing.

Errors are routing information

Category Status Retried? Use it when
transport_error 400 no the request is malformed
permission_denied 403 no policy refused this call
not_supported 501 no this adapter does not do that
model_error 500 no valid input, the model failed
provider_error 502 yes an upstream dependency failed
overloaded 503 yes backpressure — come back later

Classifying a bad frame as a model_error turns one dropped frame into a retry storm. Classifying a genuine overload as a model_error gets your adapter reported as broken.

Permissions are a contract with the operator

Permissions in /capabilities declares what the adapter needs: a GPU, egress to named hosts, host filesystem paths. KAI-C refuses to register an adapter asking for more than the operator granted — so over-declaring blocks the install, and an undeclared egress host turning up in an audit log gets the adapter removed.

Declare exactly what you use, and say so in the listing summary.

Body shapes

Which decorator you use determines how /infer parses a request, and therefore what the published OpenAPI document tells a caller to send.

Decorator Body shape Multipart field JSON field
@adapter.on_image IMAGE frame frame_b64
@adapter.on_audio AUDIO audio audio_b64
@adapter.on_data GENERIC data data_b64
@adapter.on_text TEXT the JSON body itself

KAI-C sends multipart; the base64 route exists so a shell, a test or a quick curl can reach the same endpoint.