Streaming inference¶
POST /infer is one HTTP round-trip per frame — fine at a frame every
few seconds, wasteful at twenty a second. A stream is one session, one
warm model, and one correlation id for the whole episode, which makes a
sequence of frames traceable as a single event rather than N unrelated
inferences.
It is also the only way an adapter can push back: pause and resume
are how a model that cannot keep up says so.
Declaring it¶
@adapter.on_stream()
async def stream(websocket):
...
Declaring the handler advertises streaming in /capabilities and
publishes the protocol at /asyncapi.json. An adapter without it
answers /infer/stream with a 501 carrying the §7 envelope, rather than
leaving the caller to guess.
The protocol¶
client adapter
│ ── handshake ─────────────────▶│ camera, task, transport
│ ◀──────────── handshake_ack ──│ session id, correlation id
│ ── frame + binary ────────────▶│
│ ◀──────────────────── result ──│ echoes seq
│ ◀───────────────────── pause ──│ backpressure
│ ◀──────────────────── resume ──│
│ ── close ─────────────────────▶│
Every message is a contract type in opennvr_adapter_sdk.contract, so
validation is a model_validate_json rather than hand-parsing, and the
AsyncAPI document is generated from the same classes.
Echo seq on every result: it is how the client pairs an answer with
the frame that produced it when several are in flight.
Full example:
05_streaming.py.