{ "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://git.octoturge.com/Continuum/continuum-schemas/raw/branch/main/schemas/continuum/v1/ai/frame_task.schema.json", "title": "FrameTask", "description": "One camera still, handed from continuum-backend to continuum-ai-worker for print-failure scoring. Produced by XADD onto the stream named below; consumed by XREADGROUP.\n\nThis is a Valkey stream entry, not a JSON document: the wire form is a flat field/value map and EVERY value is a string. That is why there are no numeric or boolean properties here and why a field with no value is omitted from the XADD rather than written as an empty string - an empty string is a value, and a consumer that treats it as one ends up looking up the object key \"\".\n\nField names are snake_case, matching every other schema in this repository. The backend's HTTP routes are camelCase; this wire is not an HTTP route, and the consumer on the other end is serde-derived Rust.", "x-stream": "stream:camera_frames", "x-consumer-group": "ai-worker", "type": "object", "properties": { "farm_id": { "type": "string", "format": "uuid", "description": "`farms.id`. Carried so a consumer can attribute load and alerts per farm without a database round trip." }, "printer_id": { "type": "string", "format": "uuid", "description": "`printers.id` - the backend's own UUID, already resolved from the gateway's printer id. NOT the gateway's `printers.toml` id, which means nothing outside its own farm." }, "print_job_id": { "type": "string", "format": "uuid", "description": "`print_jobs.id`, when the frame belongs to a tracked job. Omitted entirely when it does not - a printer may be streaming while idle, or running something queued outside Continuum. Consumers must treat an absent key and an empty value alike, and must not fabricate a job id." }, "r2_object_key": { "type": "string", "minLength": 1, "description": "Where the frame actually is, in the R2 bucket. The frame bytes are deliberately NOT in the stream: a stream entry is held in memory by the server and replayed to every consumer group, and a JPEG per printer per second would make the stream the largest thing in the deployment." }, "captured_at": { "type": "string", "format": "date-time", "description": "When the camera took the frame, from the gateway's clock - not when the backend enqueued it. These can differ by a lot: a gateway buffers frames while its uplink is down, so a task may arrive minutes after the moment it describes." } }, "required": [ "farm_id", "printer_id", "r2_object_key", "captured_at" ], "additionalProperties": false }