Skip to content

arkataev/face-encode

Repository files navigation

Face Encoding API

A face encoding verification service built with FastAPI. Clients create a container (a verification session), upload up to 5 images, and poll for 128-dimension face encoding vectors extracted asynchronously by an external encoding service.

How It Works

  1. Create a container — a logical grouping for one verification attempt
  2. Upload images — each upload returns immediately with status "processing"
  3. Poll the summary — encodings appear as the external service processes each image

Image encoding is delegated to an external service. Processing happens in the background — the upload endpoint never blocks on encoding. Failed images store the error and still count toward the 5-image limit. There is no retry mechanism.

Data Model

erDiagram
    containers ||--o{ images : has
    images ||--o{ encodings : has

    containers {
        string id PK "UUID v4"
        datetime created_at
    }

    images {
        string id PK "UUID v4"
        string container_id FK "CASCADE delete"
        enum status "processing | completed | failed"
        datetime created_at
        string error "null unless failed"
    }

    encodings {
        string id PK "UUID v4 (face_id)"
        string image_id FK "CASCADE delete"
        blob vector "128 doubles, struct-packed"
    }
Loading

Sequence Diagram

sequenceDiagram
    participant Client
    participant API
    participant Service
    participant DB
    participant Encoding as Encoding Service

    Client->>API: POST /v1/containers
    API->>Service: create_container()
    Service->>DB: INSERT container
    API-->>Client: 201 {container_id, created_at}

    Client->>API: POST /v1/containers/{id}/images
    API->>Service: add_image(id, image_data)
    Service->>DB: SELECT container (existence check)
    Service->>DB: INSERT image (atomic count + insert)
    Service-->>API: ImageRecord (status: processing)
    API-->>Client: 202 {image_id, status: processing}

    Note right of Service: Background task (fire-and-forget)
    Service--)Encoding: POST /v1/selfie (image bytes)
    Encoding--)Service: 200 [[float]] (face vectors)
    Service--)DB: UPDATE image status=completed, INSERT encodings

    Client->>API: GET /v1/containers/{id}/summary
    API->>Service: get_summary(id)
    Service->>DB: SELECT container + images + encodings
    API-->>Client: 200 {container_id, images: [...]}
Loading

Quick Start

Docker Compose (recommended)

make docker-up

This starts two services:

  • app — the Face Encoding API at http://localhost:8001
  • encoding-service — the external face encoding service at http://localhost:8000

Stop everything:

make docker-down

Local Development

Prerequisites: Python 3.12+, Poetry

poetry install
make run    # starts at http://localhost:8001 with hot reload

The encoding service must be running separately:

docker run -p 8000:8000 veriffdocker/face-encoding-test-task:latest

API

All endpoints are under /v1/containers. Interactive docs available at http://localhost:8001/docs.

Create a container

curl -X POST http://localhost:8001/v1/containers
{
  "container_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "created_at": "2026-03-25T12:00:00Z"
}

Upload an image

curl -X POST http://localhost:8001/v1/containers/{container_id}/images \
  -F "file=@photo.jpg"
{
  "image_id": "f1e2d3c4-b5a6-7890-abcd-ef1234567890",
  "status": "processing",
  "created_at": "2026-03-25T12:00:01Z"
}

Get summary

Poll until no images have status: "processing".

curl http://localhost:8001/v1/containers/{container_id}/summary
{
  "container_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "created_at": "2026-03-25T12:00:00Z",
  "images": [
    {
      "image_id": "f1e2d3c4-b5a6-7890-abcd-ef1234567890",
      "status": "completed",
      "created_at": "2026-03-25T12:00:01Z",
      "encodings": [
        {
          "face_id": "11223344-5566-7788-99aa-bbccddeeff00",
          "vector": [0.037, -0.148, 0.020, "... 128 floats total"]
        }
      ],
      "error": null
    }
  ]
}

Error responses

All errors use {"error": "message"} format:

Status Meaning
400 No image file provided
404 Container not found
422 Container image limit reached (max 5)

Configuration

Environment Variable Default Description
ENCODING_SERVICE_URL http://localhost:8000/v1/selfie External encoding service endpoint
DATABASE_URL sqlite+aiosqlite:///face_encoding.db SQLAlchemy database URL

Testing

make test              # unit + service + API tests (no Docker needed)
make test-integration  # integration tests (starts encoding service via Docker Compose)
make lint              # black, isort, flake8, mypy

Assumptions and Limitations

  • SQLite — single-file database, no concurrent write scaling. Suitable for the scope of this project.
  • No authentication — all endpoints are publicly accessible.
  • No retry — if encoding fails, the image is permanently marked as failed. Clients must create a new container to retry.
  • No graceful shutdown — background encoding tasks are fire-and-forget. In-flight tasks may be lost on restart.
  • No file size limit — uploaded images are read entirely into memory.
  • No logging — errors are stored in the database but not logged to stdout/stderr.
  • External service trust — encoding vectors from the external service are stored without validation beyond length (128 floats).

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors