// AZ-660 / AZ-661 — vendored copy of the `../detections` gRPC contract. // // The authoritative schema lives in the `../detections` repository // (per `_docs/02_document/architecture.md §10`). This vendored copy // is kept in lock-step with that schema via the `schema_version` // field on `DetectionResponse`: any breaking schema change MUST // bump the version, and the client (built against the version pinned // in `DetectionClientConfig::expected_schema_version`) MUST emit a // hard `schema_mismatch` error if the server reports a different // version. The schema version is the explicit handshake that lets // the autopilot run alongside an evolving detection service without // silently downcasting unknown response shapes. // // Wire shape (one bi-directional stream per session): // client ─► FrameRequest stream ────► server (../detections) // client ◄── DetectionResponse stream ◄── server // // `FrameRequest` carries the encoded pixel buffer and the source // frame's monotonic timestamp; the response correlates back via // `frame_seq`. Frames with `ai_locked = true` upstream are filtered // by the client and never sent — the server therefore never sees a // FrameRequest for an AI-locked frame. syntax = "proto3"; package azaion.detection.v1; service DetectionService { // One bi-directional stream per client session. The server may // close the stream at any time; the client reconnects with // bounded backoff (`DetectionClientConfig::reconnect_*`). rpc Stream(stream FrameRequest) returns (stream DetectionResponse); } // Pixel formats mirrored from `shared::models::frame::PixelFormat`. // Encoded as a proto enum so the wire is self-describing. enum PixelFormat { PIXEL_FORMAT_UNSPECIFIED = 0; PIXEL_FORMAT_NV12 = 1; PIXEL_FORMAT_YUV420P = 2; PIXEL_FORMAT_RGB24 = 3; } // One inference request per frame. The client tracks `frame_seq` // for response correlation (the response carries the same value // in `frame_seq`). message FrameRequest { uint64 frame_seq = 1; // Capture timestamp (monotonic, ns) — used by the client to // compute per-frame round-trip latency from the response. uint64 capture_ts_monotonic_ns = 2; uint32 width = 3; uint32 height = 4; PixelFormat pix_fmt = 5; bytes pixels = 6; } // Bounding box in [0,1] normalized coordinates (mirrors // `shared::models::frame::BoundingBox`). message BoundingBox { float x_min = 1; float y_min = 2; float x_max = 3; float y_max = 4; } // One detection inside a `DetectionResponse`. message Detection { uint32 class_id = 1; string class_name = 2; float confidence = 3; BoundingBox bbox_normalized = 4; optional bytes mask_or_polyline = 5; uint64 source_frame_seq = 6; } // Server-streamed response. `schema_version` is the handshake the // client validates against `expected_schema_version`; any mismatch // is a hard `schema_mismatch` error and the response is rejected. // `model_version` may change at runtime when the inference model // is hot-swapped — the client emits a `ModelVersionChanged` event // on the first response with a new version. message DetectionResponse { uint32 schema_version = 1; string model_version = 2; uint64 frame_seq = 3; // Server-side processing latency for THIS frame, in milliseconds. // The client also computes its own round-trip latency from // `capture_ts_monotonic_ns` so it can detect transport latency // independently of server-internal latency. uint32 latency_ms = 4; repeated Detection detections = 5; }