openapi: 3.0.3
info:
  title: Repzo API - AI Object Detection Label Report
  version: 1.0.0
  description: |
    Read-only, computed **annotation-health report for one object-detection
    label**. It has no collection of its own — every call aggregates live over
    `ai-object-detection-task` documents in the caller's namespace — and only
    `GET /ai-object-detection-label-report` (Feathers `find`) is served; `get`,
    `create`, `update`, `patch` and `delete` answer 405.

    **Default mode (`?dataset=<id>&label=<id>`).** Returns a `summary` — how
    the label's annotations in that dataset spread across subsets
    (train / val / test / auto / ignore), annotation-group states (manual /
    auto / auto_edited), usable / confirmed counts and training / validation
    eligibility — plus `items[]`, one row per annotation of the label with the
    frame image URL and dimensions, the YOLO box, confidence, group state, the
    effective subset and `included_in_training` / `included_in_val` flags.
    The summary always covers the whole dataset; the item filters (`subset`,
    `annotation_state`, `label_state`, `usable`, `confirmed`, `included`) and
    the `per_page` / `page` pagination apply to `items[]` only, and
    `total_result` / `total_pages` count the filtered stream. Eligibility
    mirrors the training-set builder: a group counts when
    `usable && confirmed && annotation_state ∈ {manual, auto, auto_edited}`
    (`mode=all`, the model default) or `∈ {manual, auto_edited}`
    (`mode=smart`); the task's subset (≠ ignore) then routes it to train / val.

    **Dims mode (`?mode=dims&label=<id>`).** The label screen's *smart dims
    analyzer*: collects the label's most recent placed annotations that carry
    a measured `world_size`, rejects low-depth-confidence rows and MAD-based
    outliers (modified z-score above `z_threshold` on either axis), and
    proposes new `physical_size` dims (cm) as the mean of the kept samples —
    only when at least `min_samples` survive. The response is a single object
    (label, effective params, robust stats, every sample with its verdict);
    it is what an admin reviews before writing `physical_size` back onto the
    label with `PUT /ai-object-detection-label/{id}`.

    **Who calls it.** Back-office admins (dataset / label screens). Scoped to
    the caller's `company_namespace` by the global auth hook; disabled tasks
    and tasks without a real image are excluded. Relates to
    `ai-object-detection-label`, `ai-object-detection-dataset` and
    `ai-object-detection-task`.
servers:
  - url: https://sv.api.repzo.me
security:
  - ApiKeyAuth: []
  - JwtAuth: []
paths:
  /ai-object-detection-label-report:
    get:
      summary: Label annotation-health report, or smart dims analysis
      description: |
        Without `mode=dims`: `dataset` and `label` are required and the
        response is `LabelReportResult`. With `mode=dims`: only `label` is
        required and the response is `LabelDimsResult`.
      operationId: findAiObjectDetectionLabelReport
      parameters:
        - in: query
          name: mode
          description: |
            `all` (default) / `smart` select the eligibility predicate of the
            annotation-health report; `dims` switches to the smart dims
            analyzer. Any other value is treated as `all`.
          schema:
            type: string
            enum: [all, smart, dims]
            default: all
        - in: query
          name: label
          required: true
          description: "`ai-object-detection-label` id (24-hex ObjectId). Required in both modes."
          schema: { type: string }
        - in: query
          name: dataset
          description: "`ai-object-detection-dataset` id (24-hex ObjectId). Required unless `mode=dims`."
          schema: { type: string }
        - in: query
          name: subset
          description: Item filter (default mode) — effective subset. Pass once or as `?subset[]=`.
          schema:
            oneOf:
              - type: string
                enum: [train, val, test, auto, ignore]
              - type: array
                items:
                  type: string
                  enum: [train, val, test, auto, ignore]
        - in: query
          name: annotation_state
          description: Item filter (default mode) — `group.annotation_state`.
          schema:
            oneOf:
              - type: string
                enum: [auto, manual, auto_edited]
              - type: array
                items:
                  type: string
                  enum: [auto, manual, auto_edited]
        - in: query
          name: label_state
          description: Item filter (default mode) — the annotation's `label_state`.
          schema:
            oneOf:
              - type: string
                enum: [auto, manual]
              - type: array
                items:
                  type: string
                  enum: [auto, manual]
        - in: query
          name: usable
          description: Item filter (default mode) — `group.usable`.
          schema: { type: boolean }
        - in: query
          name: confirmed
          description: Item filter (default mode) — `group.confirmed`.
          schema: { type: boolean }
        - in: query
          name: included
          description: Item filter (default mode) — only rows included in training or validation.
          schema:
            type: string
            enum: [train, val]
        - in: query
          name: per_page
          description: Page size for `items[]` (default mode; capped by the server's pagination max).
          schema: { type: integer, minimum: 1 }
          example: 50
        - in: query
          name: page
          description: 1-based page for `items[]` (default mode).
          schema: { type: integer, minimum: 1 }
          example: 1
        - in: query
          name: sample
          description: Dims mode — most recent measured annotations to consider. Clamped to 1..500.
          schema: { type: integer, minimum: 1, maximum: 500, default: 100 }
        - in: query
          name: z_threshold
          description: Dims mode — modified z-score cut-off on either axis.
          schema: { type: number, default: 3.5 }
        - in: query
          name: min_depth_confidence
          description: Dims mode — minimum `depth_confidence` (0..1) for a sample to qualify.
          schema: { type: number, default: 0.5 }
        - in: query
          name: min_samples
          description: Dims mode — minimum kept samples before `proposed` dims are returned.
          schema: { type: integer, default: 8 }
      responses:
        "200":
          description: "`LabelReportResult` in the default mode, `LabelDimsResult` when `mode=dims`."
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/LabelReportResult"
                  - $ref: "#/components/schemas/LabelDimsResult"
        "400":
          description: "`dataset` / `label` missing or not a valid ObjectId."
components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: api-key
      description: |
        Server-issued API key. Also accepted via the `x-api-key` header or the
        `?apiKey=` query parameter as fallbacks.
    JwtAuth:
      type: apiKey
      in: header
      name: Authorization
      description: |
        Raw JWT in the `Authorization` header — **no `Bearer ` prefix**.
        Obtained from `POST /authenticate` (admin / rep / client login).
  schemas:
    Box:
      type: object
      description: YOLO-style pixel box on the frame image.
      properties:
        x1: { type: number }
        y1: { type: number }
        x2: { type: number }
        y2: { type: number }
    LabelReportItem:
      type: object
      description: One annotation of the requested label inside the requested dataset.
      properties:
        task_id:
          type: string
          description: "`ai-object-detection-task` id."
        image_url:
          type: string
          description: Public URL of the frame image.
        image_w:
          type: number
          description: Image width (task `shape[0]`, falling back to the media document).
        image_h:
          type: number
          description: Image height (task `shape[1]`, falling back to the media document).
        subset:
          type: string
          enum: [train, val, test, auto, ignore]
          description: Subset from THIS dataset's `task_dataset` entry.
        box:
          $ref: "#/components/schemas/Box"
        confidence: { type: number }
        label_state:
          type: string
          enum: [auto, manual]
        group:
          type: object
          properties:
            usable: { type: boolean }
            confirmed: { type: boolean }
            annotation_state:
              type: string
              enum: [auto, manual, auto_edited]
            engine:
              type: string
              enum: [trained, zero_shot]
            model_version:
              type: string
              description: "`ai-object-detection-model-version` id when `engine` is `trained`."
        included_in_training:
          type: boolean
          description: "`usable && confirmed && state ∈ mode set` AND subset ∈ {train, auto}."
        included_in_val:
          type: boolean
          description: "`usable && confirmed && state ∈ mode set` AND subset ∈ {val, auto}."
        _qualifies:
          type: boolean
          description: Internal eligibility flag the pipeline leaves on the row.
    LabelReportSummary:
      type: object
      description: Distribution over ALL of the label's annotations in the dataset (item filters do not apply).
      properties:
        annotations_total: { type: integer }
        tasks_total:
          type: integer
          description: Distinct tasks carrying the label.
        subset_train: { type: integer }
        subset_val: { type: integer }
        subset_test: { type: integer }
        subset_auto: { type: integer }
        subset_ignore: { type: integer }
        state_manual: { type: integer }
        state_auto: { type: integer }
        state_auto_edited: { type: integer }
        usable_count: { type: integer }
        confirmed_count: { type: integer }
        training_eligible: { type: integer }
        val_eligible: { type: integer }
    LabelReportResult:
      type: object
      description: Default-mode response.
      properties:
        summary:
          $ref: "#/components/schemas/LabelReportSummary"
        items:
          type: array
          items:
            $ref: "#/components/schemas/LabelReportItem"
        mode:
          type: string
          enum: [all, smart]
        total_result:
          type: integer
          description: Items matching the item filters (the paginated stream).
        current_page: { type: integer }
        per_page: { type: integer }
        total_pages: { type: integer }
    LabelDimsSample:
      type: object
      description: One measured annotation considered by the dims analyzer.
      properties:
        task:
          type: string
          description: "`ai-object-detection-task` id."
        session:
          type: string
          description: "Parent `ai-object-detection-session` id (session frames only)."
        frame_id: { type: integer }
        createdAt: { type: string, format: date-time }
        w_cm:
          type: number
          description: Measured physical width (cm).
        h_cm:
          type: number
          description: Measured physical height (cm).
        confidence: { type: number }
        depth_confidence:
          type: number
          description: 0..1 depth confidence of the placement.
        label_state:
          type: string
          enum: [auto, manual]
        z_w:
          type: number
          nullable: true
          description: Modified z-score of `w_cm` (2 dp); `null` when it is not finite.
        z_h:
          type: number
          nullable: true
        kept:
          type: boolean
          description: Not an outlier and depth confidence at/above `min_depth_confidence`.
        reject_reason:
          type: string
          nullable: true
          enum: [low_depth_confidence, outlier, null]
    LabelDimsResult:
      type: object
      description: "`mode=dims` response."
      properties:
        label:
          type: object
          nullable: true
          description: The analysed label (projection).
          properties:
            _id: { type: string }
            name: { type: string }
            label_group: { type: string }
            physical_size:
              type: object
              properties:
                w_cm: { type: number }
                h_cm: { type: number }
        params:
          type: object
          description: Effective parameters after defaults / clamping.
          properties:
            sample: { type: integer }
            z_threshold: { type: number }
            min_depth_confidence: { type: number }
            min_samples: { type: integer }
        stats:
          type: object
          properties:
            n:
              type: integer
              description: Samples collected.
            n_qualified:
              type: integer
              description: Samples with depth confidence at/above `min_depth_confidence`.
            n_kept:
              type: integer
              description: Samples kept after outlier rejection.
            median_w_cm: { type: number }
            median_h_cm: { type: number }
            mad_w_cm:
              type: number
              description: Median absolute deviation (2 dp).
            mad_h_cm: { type: number }
            proposed:
              type: object
              nullable: true
              description: Mean of kept samples (1 dp); `null` when fewer than `min_samples` were kept.
              properties:
                w_cm: { type: number }
                h_cm: { type: number }
        samples:
          type: array
          items:
            $ref: "#/components/schemas/LabelDimsSample"
