Skip to content

01. Product & System Overview

What the product does

Universal OCR Service converts complex documents (PDFs and common image formats) into developer-friendly Markdown and HTML, with optional per-page block metadata. It is optimized for layout-heavy pages: tables, formulas, charts, and figures.

Confirmed product framing: README.md, package description in pyproject.toml (paddle-ocr-service).

Primary users and workflows

User Workflow
Dashboard operator Upload file → pick provider mode → watch progress → preview / download
API consumer POST /ocr/jobs → poll GET .../statusGET .../download/{md\|html\|zip}
Operator / developer make start or docker compose -f docker-compose-full.yml up

Primary request-to-result flow

%%{init: {
  "theme": "base",
  "themeVariables": {
    "primaryColor": "#1e293b",
    "primaryTextColor": "#f8fafc",
    "primaryBorderColor": "#38bdf8",
    "lineColor": "#64748b",
    "actorBackground": "#1e293b",
    "actorBorder": "#38bdf8",
    "actorTextColor": "#f8fafc",
    "labelBoxBorderColor": "#38bdf8",
    "labelBoxBkgColor": "#1e293b",
    "labelTextColor": "#f8fafc",
    "noteBorderColor": "#fbbf24",
    "noteBkgColor": "#78350f",
    "noteTextColor": "#fffbeb"
  }
}}%%
sequenceDiagram
  actor User
  participant UI as React UI
  participant API as FastAPI
  participant R as Redis
  participant C as Celery
  participant P as Provider

  User->>UI: Select file + vlm_mode
  UI->>API: POST /ocr/jobs
  API->>API: Save jobs/{id}/input.*
  API->>R: HSET job:{id} status=pending
  API->>C: run_ocr_job.delay(...)
  API-->>UI: 202 {job_id}
  loop Every 2s
    UI->>API: GET /ocr/jobs/{id}/status
    API->>R: HGETALL job:{id}
    API-->>UI: progress fields
  end
  C->>R: status=processing
  C->>P: Layout/VLM or third-party OCR
  P-->>C: Text / artifacts
  C->>C: Write result.md/html + metadata
  C->>R: status=done
  UI->>API: download md + html
  API-->>UI: Preview tabs

Major subsystems

Subsystem Responsibility Evidence
FastAPI app Job CRUD, status, downloads, health app/main.py, app/api/ocr.py
Celery worker Run OCR pipeline, update Redis, write files app/tasks/ocr_task.py
Redis Broker (DB 1) + job hashes (DB 0) app/tasks/celery_app.py, app/api/ocr.py
Pipeline core Layout + parallel block VLM app/services/pipeline.py, paddleocr_pipeline.py
Provider extractors Whole-doc NVIDIA / Sarvam / raw VLM app/services/{nvidia,sarvam,vlm}/
Storage helpers Job dirs, UUID validation, metadata JSON app/services/storage.py
React dashboard Upload, poll, history, dual preview frontend/src/App.tsx

High-level architecture

graph TD
  classDef default fill:#1e293b,stroke:#38bdf8,stroke-width:2px,color:#f8fafc
  classDef highlight fill:#065f46,stroke:#34d399,stroke-width:2px,color:#f0fdf4
  classDef warning fill:#78350f,stroke:#fbbf24,stroke-width:2px,color:#fffbeb

  Client["Browser / curl"]:::highlight
  API["FastAPI<br/>uvicorn"]
  Worker["Celery worker"]
  Redis["Redis 7"]
  Disk["Local jobs/ volume"]
  Ext["LiteLLM / NIM / Sarvam / Paddle VL server"]:::warning

  Client --> API
  API --> Redis
  API --> Disk
  API --> Worker
  Worker --> Redis
  Worker --> Disk
  Worker --> Ext

  linkStyle default stroke:#64748b,stroke-width:2px

Provider modes at a glance

Mode Behavior Needs
layout PP-DocLayoutV3 → per-block VLM (Paddle or LiteLLM) Layout model; Paddle server or VLM_MODEL
vlm Whole pages to LiteLLM via Jinja prompt VLM_MODEL + provider keys
nvidia NeMo Retriever Parse per page NVIDIA_OCR_API_KEY
sarvam Sarvam async job → md/html/json/zip SARVAM_API_KEY

Per-job override: query param vlm_mode on POST /ocr/jobs (app/api/ocr.py).

Key technical bets and tradeoffs

Bet Tradeoff
Async Celery for all OCR More moving parts than sync FastAPI; needed for long PDFs
Ephemeral Redis + disk Simple ops; not a DMS; TTL vs disk can diverge
Pluggable providers behind one API Mode-specific quality/latency; shared metadata is uneven
Polling UI Simple and robust; not instant push
Paddle on amd64 Docker Works on Apple Silicon via emulation; slower

Documented in docs/decisions/architectural-decisions.md.

Interview soundbite — "What did you build?"

I built an asynchronous OCR microservice that turns PDFs and images into Markdown and HTML. FastAPI handles job submission and status; Celery workers run either a layout-aware Paddle + VLM pipeline or third-party OCR APIs (NVIDIA, Sarvam). Redis tracks progress; results are files on disk. A React dashboard lets you pick a provider, watch per-block progress, and preview dual HTML/Markdown output.