From 8dd917b6ede888afacf3dda0763d189b4442d19b Mon Sep 17 00:00:00 2001 From: Pier-Jean Malandrino Date: Thu, 2 Apr 2026 21:40:02 +0200 Subject: [PATCH 1/7] Split requirements into base and local variants Remote mode only needs FastAPI, httpx and lightweight deps. Local mode adds docling and docling-core for in-process conversion. --- document-parser/requirements-local.txt | 3 +++ document-parser/requirements.txt | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 document-parser/requirements-local.txt diff --git a/document-parser/requirements-local.txt b/document-parser/requirements-local.txt new file mode 100644 index 0000000..82cf543 --- /dev/null +++ b/document-parser/requirements-local.txt @@ -0,0 +1,3 @@ +-r requirements.txt +docling>=2.80.0,<3.0.0 +docling-core>=2.0.0,<3.0.0 diff --git a/document-parser/requirements.txt b/document-parser/requirements.txt index 4b8cc74..24aeedb 100644 --- a/document-parser/requirements.txt +++ b/document-parser/requirements.txt @@ -1,5 +1,3 @@ -docling>=2.80.0,<3.0.0 -docling-core>=2.0.0,<3.0.0 fastapi>=0.115.0,<1.0.0 uvicorn[standard]>=0.32.0,<1.0.0 python-multipart>=0.0.12 From 9ac0840607854e916ae50491c2ed9a0cb13a52e7 Mon Sep 17 00:00:00 2001 From: Pier-Jean Malandrino Date: Thu, 2 Apr 2026 21:40:45 +0200 Subject: [PATCH 2/7] Add multi-target Dockerfiles for remote and local builds Both root and backend Dockerfiles now use a shared base stage with two targets: remote (lightweight, ~300MB) and local (full Docling, ~2-3GB). docker-compose uses CONVERSION_MODE to select the target. --- Dockerfile | 35 +++++++++++++++++++++++++---------- docker-compose.yml | 3 +++ document-parser/Dockerfile | 34 +++++++++++++++++++++++++++++++--- 3 files changed, 59 insertions(+), 13 deletions(-) diff --git a/Dockerfile b/Dockerfile index bbfbe36..a18a14c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,10 @@ # syntax=docker/dockerfile:1 # ============================================================================= -# Docling Studio — single-image build (frontend + backend) +# Docling Studio — single-image build (frontend + backend, multi-target) # # Usage: -# docker build -t docling-studio . -# docker run -p 3000:3000 docling-studio +# docker build --target remote -t docling-studio:remote . +# docker build --target local -t docling-studio:local . # ============================================================================= # --- Stage 1: Build frontend assets --- @@ -18,21 +18,19 @@ RUN npm ci COPY frontend/ . RUN VITE_APP_VERSION=${APP_VERSION} npm run build -# --- Stage 2: Runtime (Python + Nginx) --- -FROM python:3.12-slim +# --- Stage 2: Base runtime (Python + Nginx) --- +FROM python:3.12-slim AS base ARG APP_VERSION=dev ENV APP_VERSION=${APP_VERSION} -# System deps: poppler (pdf2image), nginx, and OpenCV runtime libs +# System deps: poppler (pdf2image), nginx RUN apt-get update && apt-get install -y --no-install-recommends \ poppler-utils \ - libgl1 \ - libglib2.0-0 \ nginx \ && rm -rf /var/lib/apt/lists/* -# Python deps +# Python deps (common) WORKDIR /app COPY document-parser/requirements.txt . RUN pip install --no-cache-dir -r requirements.txt @@ -57,5 +55,22 @@ ENV DB_PATH=/app/data/docling_studio.db EXPOSE 3000 -# nginx needs to run as root for port binding, then drops to appuser for uvicorn CMD ["sh", "-c", "nginx && exec su appuser -c 'uvicorn main:app --host 127.0.0.1 --port 8000'"] + +# --- Remote: lightweight, delegates to Docling Serve --- +FROM base AS remote +ENV CONVERSION_ENGINE=remote + +# --- Local: full Docling in-process --- +FROM base AS local + +RUN apt-get update && apt-get install -y --no-install-recommends \ + libgl1 \ + libglib2.0-0 \ + && rm -rf /var/lib/apt/lists/* + +COPY document-parser/requirements-local.txt . +RUN pip install --no-cache-dir -r requirements-local.txt + +RUN chown -R appuser:appuser /app +ENV CONVERSION_ENGINE=local diff --git a/docker-compose.yml b/docker-compose.yml index 6e75d5e..f1f80aa 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,6 +2,7 @@ services: document-parser: build: context: ./document-parser + target: ${CONVERSION_MODE:-local} expose: - "8000" volumes: @@ -9,6 +10,8 @@ services: - db_data:/app/data environment: CORS_ORIGINS: ${CORS_ORIGINS:-http://localhost:3000,http://localhost:5173} + DOCLING_SERVE_URL: ${DOCLING_SERVE_URL:-} + DOCLING_SERVE_API_KEY: ${DOCLING_SERVE_API_KEY:-} deploy: resources: limits: diff --git a/document-parser/Dockerfile b/document-parser/Dockerfile index 76cc697..a9c3b17 100644 --- a/document-parser/Dockerfile +++ b/document-parser/Dockerfile @@ -1,9 +1,17 @@ -FROM python:3.12-slim +# syntax=docker/dockerfile:1 +# ============================================================================= +# Docling Studio — backend image (multi-target: remote / local) +# +# Usage: +# docker build --target remote -t docling-studio-backend:remote . +# docker build --target local -t docling-studio-backend:local . +# ============================================================================= + +# --- Base: common deps for both targets --- +FROM python:3.12-slim AS base RUN apt-get update && apt-get install -y --no-install-recommends \ poppler-utils \ - libgl1 \ - libglib2.0-0 \ && rm -rf /var/lib/apt/lists/* WORKDIR /app @@ -24,3 +32,23 @@ ENV DB_PATH=/app/data/docling_studio.db USER appuser CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] + +# --- Remote: lightweight, delegates to Docling Serve --- +FROM base AS remote +ENV CONVERSION_ENGINE=remote + +# --- Local: full Docling in-process --- +FROM base AS local + +USER root +RUN apt-get update && apt-get install -y --no-install-recommends \ + libgl1 \ + libglib2.0-0 \ + && rm -rf /var/lib/apt/lists/* + +COPY requirements-local.txt . +RUN pip install --no-cache-dir -r requirements-local.txt + +RUN chown -R appuser:appuser /app +USER appuser +ENV CONVERSION_ENGINE=local From 3dd7470562b0c776936d9592519b80e080698b41 Mon Sep 17 00:00:00 2001 From: Pier-Jean Malandrino Date: Thu, 2 Apr 2026 21:41:04 +0200 Subject: [PATCH 3/7] Split release pipeline into remote and local image builds Matrix strategy builds both targets in parallel. Tags follow the pattern: v0.3.0-remote, v0.3.0-local, latest-remote, latest-local. --- .github/workflows/release.yml | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9066a50..a50c6ed 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,4 +1,4 @@ -name: Release Docker Image +name: Release Docker Images on: push: @@ -11,12 +11,16 @@ env: jobs: release: - name: Build & push Docker image + name: Build & push — ${{ matrix.target }} runs-on: ubuntu-latest permissions: contents: read packages: write + strategy: + matrix: + target: [remote, local] + steps: - uses: actions/checkout@v4 @@ -28,15 +32,15 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Extract version from tag + - name: Docker metadata id: meta uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} tags: | - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - type=raw,value=latest + type=semver,pattern={{version}},suffix=-${{ matrix.target }} + type=semver,pattern={{major}}.{{minor}},suffix=-${{ matrix.target }} + type=raw,value=latest-${{ matrix.target }} - name: Extract version from tag id: version @@ -45,6 +49,7 @@ jobs: - uses: docker/build-push-action@v6 with: context: . + target: ${{ matrix.target }} push: true platforms: linux/amd64,linux/arm64 build-args: APP_VERSION=${{ steps.version.outputs.value }} From aa7c539cc3245dc8342ca25df611a00f0c2b4007 Mon Sep 17 00:00:00 2001 From: Pier-Jean Malandrino Date: Thu, 2 Apr 2026 21:47:34 +0200 Subject: [PATCH 4/7] Update documentation for remote/local image variants Reflect the two Docker targets across README, getting-started, contributing guides, and .env.example with new configuration variables (CONVERSION_ENGINE, DOCLING_SERVE_URL, DOCLING_SERVE_API_KEY). --- .env.example | 10 ++++++ CONTRIBUTING.md | 17 +++++++-- README.md | 48 +++++++++++++++++++------ docs/contributing.md | 6 ++++ docs/getting-started.md | 80 ++++++++++++++++++++++++++++++----------- 5 files changed, 127 insertions(+), 34 deletions(-) diff --git a/.env.example b/.env.example index 8ff39a2..0821ea4 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1,13 @@ +# Conversion engine: "local" (in-process Docling) or "remote" (Docling Serve) +# CONVERSION_ENGINE=local + +# Docling Serve settings (remote mode only) +# DOCLING_SERVE_URL=http://localhost:5001 +# DOCLING_SERVE_API_KEY= + +# Max seconds per conversion (default: 600) +# CONVERSION_TIMEOUT=600 + # CORS (comma-separated origins, only needed for custom deployments) # CORS_ORIGINS=http://localhost:3000,https://your-domain.com diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bea8895..50b4605 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -22,7 +22,13 @@ Thank you for your interest in contributing to Docling Studio! This guide will h ```bash cd document-parser python -m venv .venv && source .venv/bin/activate + +# Remote mode (lightweight — delegates to Docling Serve) pip install -r requirements.txt + +# Local mode (full — runs Docling in-process) +pip install -r requirements-local.txt + pip install ruff pytest pytest-asyncio httpx # dev tools uvicorn main:app --reload --port 8000 ``` @@ -129,11 +135,16 @@ We use [Semantic Versioning](https://semver.org/): `MAJOR.MINOR.PATCH`. ### Docker Image Tags +Each release produces two image variants: + | Tag | Description | |-----|-------------| -| `X.Y.Z` | Exact version | -| `X.Y` | Latest patch of this minor | -| `latest` | Latest stable release | +| `X.Y.Z-remote` | Exact version — lightweight (Docling Serve) | +| `X.Y.Z-local` | Exact version — full (in-process Docling) | +| `X.Y-remote` | Latest patch of this minor — lightweight | +| `X.Y-local` | Latest patch of this minor — full | +| `latest-remote` | Latest stable — lightweight | +| `latest-local` | Latest stable — full | ### Hotfix diff --git a/README.md b/README.md index 5ac1323..0a70625 100644 --- a/README.md +++ b/README.md @@ -85,22 +85,42 @@ frontend/src/ ## Quick Start -### Docker (fastest) +Docling Studio ships two Docker image variants: + +| Variant | Image tag | Size | Description | +|---------|-----------|------|-------------| +| **remote** | `latest-remote` | ~300 MB | Lightweight — delegates to an external [Docling Serve](https://github.com/DS4SD/docling-serve) instance | +| **local** | `latest-local` | ~2–3 GB | Full — runs Docling in-process (downloads ML models on first run) | + +### Docker — remote mode (fastest) ```bash -docker run -p 3000:3000 ghcr.io/scub-france/docling-studio:latest +docker run -p 3000:3000 \ + -e DOCLING_SERVE_URL=http://your-docling-serve:5001 \ + ghcr.io/scub-france/docling-studio:latest-remote ``` -Open [http://localhost:3000](http://localhost:3000) +### Docker — local mode (self-contained) + +```bash +docker run -p 3000:3000 ghcr.io/scub-france/docling-studio:latest-local +``` > **Note:** The first analysis takes longer as Docling downloads its ML models (~400 MB). Subsequent runs are fast. +Open [http://localhost:3000](http://localhost:3000) + ### Docker Compose (for development) ```bash git clone https://github.com/scub-france/Docling-Studio.git cd Docling-Studio + +# Local mode (default) docker compose up --build + +# Remote mode +CONVERSION_MODE=remote DOCLING_SERVE_URL=http://your-docling-serve:5001 docker compose up --build ``` ### Local Development @@ -109,7 +129,13 @@ docker compose up --build ```bash cd document-parser python -m venv .venv && source .venv/bin/activate + +# Remote mode (lightweight) pip install -r requirements.txt + +# Local mode (with Docling) +pip install -r requirements-local.txt + uvicorn main:app --reload --port 8000 ``` @@ -156,6 +182,9 @@ All configuration is done via environment variables. See [`.env.example`](.env.e | Variable | Default | Description | |----------|---------|-------------| +| `CONVERSION_ENGINE` | `local` | `local` (in-process Docling) or `remote` (Docling Serve) | +| `DOCLING_SERVE_URL` | `http://localhost:5001` | Docling Serve endpoint (remote mode only) | +| `DOCLING_SERVE_API_KEY` | — | API key for Docling Serve (optional) | | `CORS_ORIGINS` | `http://localhost:3000,...` | CORS allowed origins (comma-separated) | | `UPLOAD_DIR` | `./uploads` | File storage directory | | `DB_PATH` | `./data/docling_studio.db` | SQLite database path | @@ -168,7 +197,7 @@ GitHub Actions pipelines (see [`.github/workflows/`](.github/workflows/)): | Workflow | Trigger | What it does | |----------|---------|--------------| | **CI** | push to `main`, pull requests | Lint + type check + Backend tests + Frontend tests + build | -| **Release** | push tag `v*` | Build & push multi-arch Docker image to `ghcr.io` | +| **Release** | push tag `v*` | Build & push **two** multi-arch Docker images (`remote` + `local`) to `ghcr.io` | | **Docs** | push to `main` (docs changes) | Build & deploy MkDocs to GitHub Pages | We follow [Semantic Versioning](https://semver.org/) with a simplified Git Flow. See [CONTRIBUTING.md](CONTRIBUTING.md) for the full release process. @@ -183,12 +212,11 @@ We follow [Semantic Versioning](https://semver.org/) with a simplified Git Flow. ### Docker Desktop settings -The document parser needs **at least 4 GB of RAM**: - -| Resource | Minimum | Recommended | -|----------|---------|-------------| -| Memory | 6 GB | 8 GB+ | -| CPUs | 4 | 8+ | +| | Remote image | Local image | +|---|---|---| +| **Image size** | ~300 MB | ~2–3 GB | +| **Memory** | 2 GB | 6 GB (recommended 8 GB+) | +| **CPUs** | 2 | 4 (recommended 8+) | ### Platform support diff --git a/docs/contributing.md b/docs/contributing.md index 17c6e7d..44412e3 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -20,7 +20,13 @@ ```bash cd document-parser python -m venv .venv && source .venv/bin/activate + + # Remote mode (lightweight — delegates to Docling Serve) pip install -r requirements.txt + + # Local mode (full — runs Docling in-process) + pip install -r requirements-local.txt + pip install ruff pytest pytest-asyncio httpx uvicorn main:app --reload --port 8000 ``` diff --git a/docs/getting-started.md b/docs/getting-started.md index cd38397..8e8bbcf 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -1,33 +1,67 @@ # Getting Started -## Docker Compose (recommended) +Docling Studio ships two Docker image variants: + +| Variant | Image tag | Size | Description | +|---------|-----------|------|-------------| +| **remote** | `latest-remote` | ~300 MB | Lightweight — delegates to an external [Docling Serve](https://github.com/DS4SD/docling-serve) instance | +| **local** | `latest-local` | ~2–3 GB | Full — runs Docling in-process (downloads ML models on first run) | + +## Docker — remote mode (fastest) + +```bash +docker run -p 3000:3000 \ + -e DOCLING_SERVE_URL=http://your-docling-serve:5001 \ + ghcr.io/scub-france/docling-studio:latest-remote +``` + +## Docker — local mode (self-contained) + +```bash +docker run -p 3000:3000 ghcr.io/scub-france/docling-studio:latest-local +``` + +> **Note:** The first analysis takes longer as Docling downloads its ML models (~400 MB). Subsequent runs are fast. + +Open [http://localhost:3000](http://localhost:3000). + +## Docker Compose (recommended for development) ```bash git clone https://github.com/scub-france/Docling-Studio.git cd Docling-Studio -docker compose up --build -``` -Open [http://localhost:3000](http://localhost:3000). +# Local mode (default) +docker compose up --build + +# Remote mode +CONVERSION_MODE=remote DOCLING_SERVE_URL=http://your-docling-serve:5001 docker compose up --build +``` ## Local Development -### Backend (Python 3.12+) +=== "Backend (Python 3.12+)" -```bash -cd document-parser -python -m venv .venv && source .venv/bin/activate -pip install -r requirements.txt -uvicorn main:app --reload --port 8000 -``` + ```bash + cd document-parser + python -m venv .venv && source .venv/bin/activate -### Frontend (Node 20+) + # Remote mode (lightweight) + pip install -r requirements.txt -```bash -cd frontend -npm install -npm run dev -``` + # Local mode (with Docling) + pip install -r requirements-local.txt + + uvicorn main:app --reload --port 8000 + ``` + +=== "Frontend (Node 20+)" + + ```bash + cd frontend + npm install + npm run dev + ``` The frontend runs on `http://localhost:3000` and proxies API calls to `http://localhost:8000`. @@ -71,6 +105,9 @@ All configuration is done via environment variables: | Variable | Default | Description | |----------|---------|-------------| +| `CONVERSION_ENGINE` | `local` | `local` (in-process Docling) or `remote` (Docling Serve) | +| `DOCLING_SERVE_URL` | `http://localhost:5001` | Docling Serve endpoint (remote mode only) | +| `DOCLING_SERVE_API_KEY` | — | API key for Docling Serve (optional) | | `CORS_ORIGINS` | `http://localhost:3000,...` | CORS allowed origins | | `UPLOAD_DIR` | `./uploads` | File storage directory | | `DB_PATH` | `./data/docling_studio.db` | SQLite database path | @@ -78,9 +115,10 @@ All configuration is done via environment variables: ## System Requirements -| Resource | Minimum | Recommended | -|----------|---------|-------------| -| Memory | 6 GB | 8 GB+ | -| CPUs | 4 | 8+ | +| | Remote image | Local image | +|---|---|---| +| **Image size** | ~300 MB | ~2–3 GB | +| **Memory** | 2 GB | 6 GB (recommended 8 GB+) | +| **CPUs** | 2 | 4 (recommended 8+) | All Docker images are multi-arch (`linux/amd64` + `linux/arm64`). No GPU required. From 48b7d5d3e8e642ffd082203efe3e347754e093d0 Mon Sep 17 00:00:00 2001 From: Pier-Jean Malandrino Date: Fri, 3 Apr 2026 09:52:22 +0200 Subject: [PATCH 5/7] Use CPU-only torch in local image to reduce size from 5.8GB to 1.9GB Install torch and torchvision from the CPU-only index before docling to avoid pulling CUDA/nvidia/triton dependencies. Update documentation with measured image sizes (270MB remote, 1.9GB local). --- Dockerfile | 3 ++- README.md | 6 +++--- docs/getting-started.md | 6 +++--- document-parser/Dockerfile | 3 ++- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/Dockerfile b/Dockerfile index a18a14c..a5b1597 100644 --- a/Dockerfile +++ b/Dockerfile @@ -70,7 +70,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ && rm -rf /var/lib/apt/lists/* COPY document-parser/requirements-local.txt . -RUN pip install --no-cache-dir -r requirements-local.txt +RUN pip install --no-cache-dir torch torchvision --index-url https://download.pytorch.org/whl/cpu \ + && pip install --no-cache-dir -r requirements-local.txt RUN chown -R appuser:appuser /app ENV CONVERSION_ENGINE=local diff --git a/README.md b/README.md index 0a70625..fda0a1a 100644 --- a/README.md +++ b/README.md @@ -89,8 +89,8 @@ Docling Studio ships two Docker image variants: | Variant | Image tag | Size | Description | |---------|-----------|------|-------------| -| **remote** | `latest-remote` | ~300 MB | Lightweight — delegates to an external [Docling Serve](https://github.com/DS4SD/docling-serve) instance | -| **local** | `latest-local` | ~2–3 GB | Full — runs Docling in-process (downloads ML models on first run) | +| **remote** | `latest-remote` | ~270 MB | Lightweight — delegates to an external [Docling Serve](https://github.com/DS4SD/docling-serve) instance | +| **local** | `latest-local` | ~1.9 GB | Full — runs Docling in-process, CPU-only (downloads ML models on first run) | ### Docker — remote mode (fastest) @@ -214,7 +214,7 @@ We follow [Semantic Versioning](https://semver.org/) with a simplified Git Flow. | | Remote image | Local image | |---|---|---| -| **Image size** | ~300 MB | ~2–3 GB | +| **Image size** | ~270 MB | ~1.9 GB | | **Memory** | 2 GB | 6 GB (recommended 8 GB+) | | **CPUs** | 2 | 4 (recommended 8+) | diff --git a/docs/getting-started.md b/docs/getting-started.md index 8e8bbcf..539dd4f 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -4,8 +4,8 @@ Docling Studio ships two Docker image variants: | Variant | Image tag | Size | Description | |---------|-----------|------|-------------| -| **remote** | `latest-remote` | ~300 MB | Lightweight — delegates to an external [Docling Serve](https://github.com/DS4SD/docling-serve) instance | -| **local** | `latest-local` | ~2–3 GB | Full — runs Docling in-process (downloads ML models on first run) | +| **remote** | `latest-remote` | ~270 MB | Lightweight — delegates to an external [Docling Serve](https://github.com/DS4SD/docling-serve) instance | +| **local** | `latest-local` | ~1.9 GB | Full — runs Docling in-process, CPU-only (downloads ML models on first run) | ## Docker — remote mode (fastest) @@ -117,7 +117,7 @@ All configuration is done via environment variables: | | Remote image | Local image | |---|---|---| -| **Image size** | ~300 MB | ~2–3 GB | +| **Image size** | ~270 MB | ~1.9 GB | | **Memory** | 2 GB | 6 GB (recommended 8 GB+) | | **CPUs** | 2 | 4 (recommended 8+) | diff --git a/document-parser/Dockerfile b/document-parser/Dockerfile index a9c3b17..bb63592 100644 --- a/document-parser/Dockerfile +++ b/document-parser/Dockerfile @@ -47,7 +47,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ && rm -rf /var/lib/apt/lists/* COPY requirements-local.txt . -RUN pip install --no-cache-dir -r requirements-local.txt +RUN pip install --no-cache-dir torch torchvision --index-url https://download.pytorch.org/whl/cpu \ + && pip install --no-cache-dir -r requirements-local.txt RUN chown -R appuser:appuser /app USER appuser From 48c5e4ea6bda45ab36ac40428ad18fdbece5de6a Mon Sep 17 00:00:00 2001 From: Pier-Jean Malandrino Date: Fri, 3 Apr 2026 10:44:20 +0200 Subject: [PATCH 6/7] Fix RapidOCR model download permission in local image Grant appuser write access to rapidocr/models directory so OCR models can be downloaded at runtime. --- Dockerfile | 3 ++- document-parser/Dockerfile | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index a5b1597..ff3e37b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -73,5 +73,6 @@ COPY document-parser/requirements-local.txt . RUN pip install --no-cache-dir torch torchvision --index-url https://download.pytorch.org/whl/cpu \ && pip install --no-cache-dir -r requirements-local.txt -RUN chown -R appuser:appuser /app +RUN chown -R appuser:appuser /app \ + && chown -R appuser:appuser /usr/local/lib/python3.12/site-packages/rapidocr/models ENV CONVERSION_ENGINE=local diff --git a/document-parser/Dockerfile b/document-parser/Dockerfile index bb63592..3c21f9a 100644 --- a/document-parser/Dockerfile +++ b/document-parser/Dockerfile @@ -50,6 +50,7 @@ COPY requirements-local.txt . RUN pip install --no-cache-dir torch torchvision --index-url https://download.pytorch.org/whl/cpu \ && pip install --no-cache-dir -r requirements-local.txt -RUN chown -R appuser:appuser /app +RUN chown -R appuser:appuser /app \ + && chown -R appuser:appuser /usr/local/lib/python3.12/site-packages/rapidocr/models USER appuser ENV CONVERSION_ENGINE=local From abe59c3cb7088bcd8b355c5a22e67346cb244597 Mon Sep 17 00:00:00 2001 From: Pier-Jean Malandrino Date: Fri, 3 Apr 2026 12:14:15 +0200 Subject: [PATCH 7/7] Fix health endpoint not reachable through nginx proxy Move /health to /api/health on backend and update the frontend feature flag store to match. Without this, the combined Docker image nginx proxy could not reach the endpoint and feature flags (chunking/prepare mode) failed to load. --- document-parser/main.py | 2 +- document-parser/tests/test_api_endpoints.py | 2 +- frontend/src/features/feature-flags/store.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/document-parser/main.py b/document-parser/main.py index aaf0275..5927c46 100644 --- a/document-parser/main.py +++ b/document-parser/main.py @@ -103,7 +103,7 @@ app.include_router(documents_router) app.include_router(analyses_router) -@app.get("/health") +@app.get("/api/health") def health(): """Health check endpoint.""" return { diff --git a/document-parser/tests/test_api_endpoints.py b/document-parser/tests/test_api_endpoints.py index 2f34518..9826e5c 100644 --- a/document-parser/tests/test_api_endpoints.py +++ b/document-parser/tests/test_api_endpoints.py @@ -26,7 +26,7 @@ def mock_analysis_service(client): class TestHealthEndpoint: def test_health(self, client): - resp = client.get("/health") + resp = client.get("/api/health") assert resp.status_code == 200 data = resp.json() assert data["status"] == "ok" diff --git a/frontend/src/features/feature-flags/store.ts b/frontend/src/features/feature-flags/store.ts index 568609d..b3b3721 100644 --- a/frontend/src/features/feature-flags/store.ts +++ b/frontend/src/features/feature-flags/store.ts @@ -44,7 +44,7 @@ export const useFeatureFlagStore = defineStore('feature-flags', () => { async function load(): Promise { try { - const data = await apiFetch('/health') + const data = await apiFetch('/api/health') engine.value = data.engine loaded.value = true error.value = null