#  SPDX-FileCopyrightText: (C) 2025 - 2026 Intel Corporation
#  SPDX-License-Identifier: Apache-2.0

openapi: 3.0.3
info:
  title: 3D Mapping Models API
  description: |
    REST API service for 3D reconstruction with build-time model selection.

    Authentication/Authorization status:
    - This service currently does not implement endpoint-level AuthN/AuthZ.
    - Protect access using trusted network boundaries, reverse proxy controls, and TLS.

    This service provides endpoints for performing 3D reconstruction from multiple input images. The model is selected at build time:
    - **MapAnything**: Universal Feed-Forward Metric 3D Reconstruction
    - **VGGT**: Visual Geometry Grounded Transformer for sparse view reconstruction

    Each container is built with a single model to avoid dependency conflicts and reduce image size.
  version: 0.1.0
  license:
    name: Apache 2.0
    url: https://www.apache.org/licenses/LICENSE-2.0.html

servers:
  - url: https://localhost:8444/v1
    description: Direct standalone access
  - url: https://localhost/api/v1/mapping
    description: Access via Apache reverse proxy when web service is running

tags:
  - name: reconstruction
    description: 3D reconstruction operations
  - name: health
    description: Health check and status
  - name: models
    description: Model information and availability

paths:
  /reconstruction:
    post:
      tags:
        - reconstruction
      summary: Perform 3D reconstruction from input images
      description: |
        Submits a 3D reconstruction job from images or video. Returns immediately with a
        `request_id`. Poll `GET /reconstruction/status/{request_id}` until state is
        `complete` or `failed`.

        **Input Requirements:**
        - At least 2-3 images for best results; maximum 50 images per request
        - Images must be JPEG or PNG; maximum 20 MB per image file
        - Video files: maximum 50 MB
        - `camera_ids` values must match pattern `^[a-zA-Z0-9_-]{1,64}$`
        - Maximum total request size: 100 MB
        - At least one of `images` or `video` is required

        **Output Formats:**
        - `glb`: Base64-encoded GLB file (available in result via status endpoint)
        - `json`: Raw reconstruction data (camera poses, intrinsics)

        **Note:** The model type is determined at container build time, not at runtime.

        **Important:** This endpoint only accepts multipart/form-data format with actual file uploads.
        JSON payloads with base64-encoded images are NOT supported.
      operationId: reconstruct3D
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                images:
                  type: array
                  items:
                    type: string
                    format: binary
                  maxItems: 50
                  description: Image files for reconstruction (JPEG or PNG; max 20 MB each; max 50 images)
                video:
                  type: string
                  format: binary
                  description: Video file for reconstruction (max 50 MB; at least one of images or video required)
                output_format:
                  type: string
                  enum: [glb, json]
                  default: glb
                  description: Output format for the reconstruction result
                mesh_type:
                  type: string
                  enum: [mesh, pointcloud]
                  default: mesh
                  description: Output type - mesh or pointcloud
                use_keyframes:
                  type: boolean
                  default: true
                  description: Whether to use keyframe extraction for video (only applies to video input)
                camera_ids:
                  type: array
                  items:
                    type: string
                    pattern: "^[a-zA-Z0-9_-]{1,64}$"
                  description: Optional camera identifiers, one per image (pattern ^[a-zA-Z0-9_-]{1,64}$)
                camera_locations:
                  type: array
                  items:
                    type: string
                  description: Optional camera location metadata as JSON strings, one per image
            encoding:
              images:
                contentType: image/jpeg, image/png
      responses:
        "200":
          description: Reconstruction job accepted and queued
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ReconstructionAckResponse"
              example:
                success: true
                request_id: "a3f1c2d4e5b6a7f8c9d0e1f2a3b4c5d6"
                state: "processing"
        "400":
          description: Bad request - validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              examples:
                no_input:
                  summary: No images or video provided
                  value:
                    success: false
                    error: "Provide images and/or video"
                too_many_images:
                  summary: Image count exceeds limit
                  value:
                    success: false
                    error: "Too many images (max 50)"
                invalid_camera_id:
                  summary: Invalid camera_id format
                  value:
                    success: false
                    error: "Invalid camera_id format"
        "413":
          description: Request entity too large
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                success: false
                error: "Request too large"
        "500":
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                success: false
                error: "Internal server error"
        "503":
          description: Service unavailable - model not loaded
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                success: false
                error: "Model mapanything not available"

  /reconstruction/status/{request_id}:
    get:
      tags:
        - reconstruction
      summary: Get reconstruction job status
      description: |
        Polls the status of a previously submitted reconstruction job.

        **States:**
        - `processing`: Job is queued or running
        - `complete`: Reconstruction succeeded; `result` contains the full output
        - `failed`: Reconstruction failed; `error` contains a generic message

        `request_id` must be a 32-character lowercase hex string as returned by
        `POST /reconstruction`.
      operationId: getReconstructionStatus
      parameters:
        - name: request_id
          in: path
          required: true
          schema:
            type: string
            pattern: "^[0-9a-f]{32}$"
          description: The reconstruction job ID returned by POST /reconstruction
          example: "a3f1c2d4e5b6a7f8c9d0e1f2a3b4c5d6"
      responses:
        "200":
          description: Status retrieved successfully
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/StatusResponse"
              examples:
                processing:
                  summary: Job still in progress
                  value:
                    success: true
                    request_id: "a3f1c2d4e5b6a7f8c9d0e1f2a3b4c5d6"
                    state: "processing"
                    message: "running inference"
                    processing_time: null
                complete:
                  summary: Job completed successfully
                  value:
                    success: true
                    request_id: "a3f1c2d4e5b6a7f8c9d0e1f2a3b4c5d6"
                    state: "complete"
                    message: "complete"
                    processing_time: 12.34
                    result:
                      success: true
                      request_id: "a3f1c2d4e5b6a7f8c9d0e1f2a3b4c5d6"
                      glb_data: "Z2xURjIAAQAAAAAMAAAA..."
                      camera_poses:
                        - rotation: [0.98, 0.0, 0.15, 0.0]
                          translation: [0.0, 0.0, 0.0]
                      intrinsics:
                        - [
                            [525.0, 0.0, 320.0],
                            [0.0, 525.0, 240.0],
                            [0.0, 0.0, 1.0],
                          ]
                      processing_time: 12.34
                      message: "complete"
                failed:
                  summary: Job failed
                  value:
                    success: true
                    request_id: "a3f1c2d4e5b6a7f8c9d0e1f2a3b4c5d6"
                    state: "failed"
                    message: "failed"
                    processing_time: null
                    error: "Reconstruction failed"
        "400":
          description: Invalid request_id format
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                success: false
                error: "Invalid request ID"
        "404":
          description: Unknown request_id
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                success: false
                error: "unknown request_id"

  /health:
    get:
      tags:
        - health
      summary: Health check endpoint
      description: |
        Returns the health status of the API service.

        Use this endpoint to:
        - Check if the service is running and the model is ready
        - Drive Kubernetes liveness/readiness probes (returns 503 when model not loaded)
      operationId: healthCheck
      responses:
        "200":
          description: Service is healthy and model is loaded
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HealthResponse"
              example:
                success: true
                status: "healthy"
                ready: true
                model: "mapanything"
                model_loaded: true
                device: "cpu"
                initialization:
                  state: "ready"
                  stage: "model_loaded"
                  progress: 100.0
                  message: "model loaded"
                  error: null

  /models:
    get:
      tags:
        - models
      summary: List available models and their status
      description: |
        Returns information about the 3D reconstruction model in this container.

        Provides:
        - Current model type (determined at build time)
        - Loading status and model information
        - Model description and capabilities
      operationId: listModels
      responses:
        "200":
          description: Models information retrieved successfully
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HealthResponse"
              example:
                success: false
                status: "unhealthy"
                model_loaded: false

components:
  schemas:
    ReconstructionRequest:
      type: object
      description: |
        Multipart form data request for 3D reconstruction.
        Note: This API only accepts multipart/form-data with actual file uploads.
        JSON payloads with base64-encoded images are NOT supported.
      properties:
        images:
          type: array
          items:
            type: string
            format: binary
          minItems: 1
          maxItems: 50
          description: Image files for reconstruction (JPEG or PNG; max 20 MB each; max 50 images)
        video:
          type: string
          format: binary
          description: Video file for reconstruction (max 50 MB; at least one of images or video required)
        output_format:
          type: string
          enum: [glb, json]
          default: glb
          description: Output format for the reconstruction result
          example: "glb"
        mesh_type:
          type: string
          enum: [mesh, pointcloud]
          default: mesh
          description: |
            Output format preference for both models:
            - mesh: Force watertight mesh generation for both models
            - pointcloud: Force point cloud output for both models
          example: "mesh"
        use_keyframes:
          type: boolean
          default: true
          description: Whether to use keyframe extraction for video (only applies to video input)
        camera_ids:
          type: array
          items:
            type: string
            pattern: "^[a-zA-Z0-9_-]{1,64}$"
          description: Optional camera identifiers, one per image (pattern ^[a-zA-Z0-9_-]{1,64}$)
        camera_locations:
          type: array
          items:
            type: string
          description: Optional camera location metadata as JSON strings, one per image

    ReconstructionAckResponse:
      type: object
      description: Immediate acknowledgement returned by POST /reconstruction
      required:
        - success
        - request_id
        - state
      properties:
        success:
          type: boolean
          description: Always true for accepted jobs
          example: true
        request_id:
          type: string
          description: Unique job identifier (32-character hex string); use with GET /reconstruction/status/{request_id}
          example: "a3f1c2d4e5b6a7f8c9d0e1f2a3b4c5d6"
        state:
          type: string
          enum: [processing]
          description: Initial state; always "processing" on acceptance
          example: "processing"

    ReconstructionResult:
      type: object
      description: Full reconstruction output; present in StatusResponse.result when state is "complete"
      properties:
        success:
          type: boolean
          example: true
        request_id:
          type: string
          example: "a3f1c2d4e5b6a7f8c9d0e1f2a3b4c5d6"
        glb_data:
          type: string
          format: byte
          nullable: true
          description: Base64-encoded GLB file (only when output_format was 'glb'; null otherwise)
          example: "Z2xURjIAAQAAAAAMAAAA..."
        camera_poses:
          type: array
          items:
            $ref: "#/components/schemas/CameraPose"
          description: Estimated camera poses for each input image
        intrinsics:
          type: array
          items:
            $ref: "#/components/schemas/CameraIntrinsics"
          description: Camera intrinsic parameters for each input image
        processing_time:
          type: number
          format: float
          description: Total processing time in seconds
          example: 12.34
        message:
          type: string
          example: "complete"

    StatusResponse:
      type: object
      description: Response from GET /reconstruction/status/{request_id}
      required:
        - success
        - request_id
        - state
      properties:
        success:
          type: boolean
          example: true
        request_id:
          type: string
          example: "a3f1c2d4e5b6a7f8c9d0e1f2a3b4c5d6"
        state:
          type: string
          enum: [processing, complete, failed]
          description: Current job state
          example: "complete"
        message:
          type: string
          description: Human-readable stage description
          example: "running inference"
        processing_time:
          type: number
          format: float
          nullable: true
          description: Elapsed processing time in seconds; null while job is in progress
          example: 12.34
        result:
          allOf:
            - $ref: "#/components/schemas/ReconstructionResult"
          nullable: true
          description: Full reconstruction output; present only when state is "complete"
        error:
          type: string
          nullable: true
          description: Generic error message; present only when state is "failed"
          example: "Reconstruction failed"

    CameraPose:
      type: object
      properties:
        rotation:
          type: array
          items:
            type: number
            format: float
          minItems: 4
          maxItems: 4
          description: Quaternion rotation [x, y, z, w]
          example: [0.98, 0.0, 0.15, 0.0]
        translation:
          type: array
          items:
            type: number
            format: float
          minItems: 3
          maxItems: 3
          description: 3D translation vector [x, y, z]
          example: [0.0, 0.0, 0.0]

    CameraIntrinsics:
      type: array
      items:
        type: array
        items:
          type: number
          format: float
        minItems: 3
        maxItems: 3
      minItems: 3
      maxItems: 3
      description: 3x3 camera intrinsic matrix [[fx, 0, cx], [0, fy, cy], [0, 0, 1]]
      example: [[525.0, 0.0, 320.0], [0.0, 525.0, 240.0], [0.0, 0.0, 1.0]]

    HealthResponse:
      type: object
      required:
        - success
        - status
        - model_loaded
      properties:
        success:
          type: boolean
          description: True if service is healthy; false if model is not loaded
          example: true
        status:
          type: string
          enum: [healthy, unhealthy]
          description: Overall health status
          example: "healthy"
        ready:
          type: boolean
          description: True when the mapping model is loaded and ready for requests
          example: true
        model:
          type: string
          description: The model available in this container
          example: "mapanything"
        model_loaded:
          type: boolean
          description: Whether the model is loaded and ready to accept requests
          example: true
        device:
          type: string
          description: Compute device being used (cpu or cuda)
          example: "cpu"
        initialization:
          $ref: "#/components/schemas/InitializationStatus"

    InitializationStatus:
      type: object
      properties:
        state:
          type: string
          description: High-level initialization state (starting, ready, failed)
          example: "starting"
        stage:
          type: string
          description: Most recent initialization stage marker
          example: "model_initializing"
        progress:
          type: number
          format: float
          minimum: 0
          maximum: 100
          description: Coarse initialization progress percentage
          example: 65.0
        message:
          type: string
          description: Human-readable initialization status message
          example: "loading mapping model"
        error:
          oneOf:
            - type: string
            - type: "null"
          description: Error details when initialization fails
          example: null
        updated_at:
          type: number
          format: float
          description: Unix epoch seconds when initialization status was last updated
          example: 1720000000.0

    ModelsResponse:
      type: object
      properties:
        model:
          type: string
          description: The model available in this container
          example: "mapanything"
        model_info:
          $ref: "#/components/schemas/ModelInfo"
          description: Information about the loaded model
        camera_pose_format:
          type: object
          description: Format description for camera poses
          properties:
            rotation:
              type: string
              example: "quaternion [x, y, z, w]"
            translation:
              type: string
              example: "vector [x, y, z]"
            coordinate_system:
              type: string
              example: "OpenCV (camera-to-world transformation, standard CV coordinates)"

    ModelInfo:
      type: object
      properties:
        name:
          type: string
          description: Model identifier
          example: "mapanything"
        description:
          type: string
          description: Human-readable description of the model
          example: "MapAnything - Apache 2.0 licensed model for metric 3D reconstruction"
        loaded:
          type: boolean
          description: Whether the model is currently loaded and available
          example: true
        native_output:
          type: string
          description: The preferred output format for this model
          example: "mesh"
        supported_outputs:
          type: array
          items:
            type: string
          description: List of supported output formats
          example: ["mesh", "pointcloud"]

    ErrorResponse:
      type: object
      required:
        - success
        - error
      properties:
        success:
          type: boolean
          description: Always false for error responses
          example: false
        error:
          type: string
          description: Error message describing what went wrong
          example: "Provide images and/or video"

  responses:
    BadRequest:
      description: Bad request - validation error
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          example:
            success: false
            error: "Provide images and/or video"

    NotFound:
      description: Endpoint not found
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          example:
            success: false
            error: "Endpoint not found"

    MethodNotAllowed:
      description: Method not allowed for this endpoint
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          example:
            success: false
            error: "Method not allowed"

    RequestTooLarge:
      description: Request entity too large
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          example:
            success: false
            error: "Request too large"

    InternalServerError:
      description: Internal server error
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          example:
            success: false
            error: "Internal server error"

# Security schemes (none required for this API)
security: []

# Additional metadata
externalDocs:
  description: GitHub Repository
  url: https://github.com/facebookresearch/map-anything
