mirror of
https://github.com/tiennm99/DocsGPT.git
synced 2026-10-03 07:11:56 +00:00
build(docker): slim backend image, static frontend image, -docling variant
Backend (arc53/docsgpt): 4.5 GB compressed -> 0.9 GB with both embedding models and tiktoken baked in. - torch/transformers gone from the default install (docling extra only). - Ubuntu 24.04 ships python3.12: no deadsnakes PPA, no software-properties- common; every pin is a wheel, so no gcc/g++/rust in the builder. - COPY --chown and a prefetch that runs as the process user replace the trailing chown -R, which duplicated the 600 MB model layer. - .dockerignore keeps __pycache__, .coverage, local indexes and .env out. - EXTRAS build arg (INSTALL_DOCLING kept as an alias); the docling variant also bakes docling's layout/table/RapidOCR models (DOCLING_ARTIFACTS_PATH) and tesseract, and drops only the discovery documents of Google APIs the app never builds. - FLASK_DEBUG env removed (unused); OCI labels added. Frontend (arc53/docsgpt-fe): 302 MB Vite dev server -> 25 MB static build behind nginx. VITE_* variables are injected at container start into /config.js and read through src/env.ts, so the image no longer needs a rebuild per deployment; docker-compose.yaml keeps hot reload via the dev target. Publishing: every release and develop build now pushes a slim tag and a -docling tag (docling engine + models + tesseract). docker-compose-hub.yaml takes DOCSGPT_IMAGE_TAG / DOCSGPT_IMAGE_VARIANT; docker-compose-standalone.yaml runs the stack from pre-built images without a checkout and is attached to each release. setup.sh selects the -docling variant for OCR instead of requiring a local build. A new workflow builds the image on PRs that touch it and runs verify_offline under --network none; lint checks the exported requirements match uv.lock.
This commit is contained in:
1 parent
4707c45b93
commit
aecb596e99
30 files changed
+706
-247
No files matched your search
+52
-34
@@ -9,24 +9,17 @@ jobs:
|
||||
if: github.repository == 'arc53/DocsGPT'
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- platform: linux/amd64
|
||||
runner: ubuntu-latest
|
||||
suffix: amd64
|
||||
- platform: linux/arm64
|
||||
runner: ubuntu-24.04-arm
|
||||
suffix: arm64
|
||||
runs-on: ${{ matrix.runner }}
|
||||
platform: [linux/amd64, linux/arm64]
|
||||
# "" is the slim default image; "-docling" bakes the docling parser
|
||||
# engine, its models and tesseract in (OCR-ready).
|
||||
variant: ["", "-docling"]
|
||||
runs-on: ${{ matrix.platform == 'linux/arm64' && 'ubuntu-24.04-arm' || 'ubuntu-latest' }}
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up QEMU # Only needed for emulation, not for native arm64 builds
|
||||
if: matrix.platform == 'linux/arm64'
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
with:
|
||||
@@ -46,6 +39,17 @@ jobs:
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Image metadata (OCI labels)
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: |
|
||||
${{ secrets.DOCKER_USERNAME }}/docsgpt
|
||||
ghcr.io/${{ github.repository_owner }}/docsgpt
|
||||
labels: |
|
||||
org.opencontainers.image.title=DocsGPT${{ matrix.variant }}
|
||||
org.opencontainers.image.version=${{ github.event.release.tag_name }}
|
||||
|
||||
- name: Build and push platform-specific images
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
@@ -53,17 +57,24 @@ jobs:
|
||||
platforms: ${{ matrix.platform }}
|
||||
context: ./application
|
||||
push: true
|
||||
build-args: |
|
||||
EXTRAS=${{ matrix.variant == '-docling' && 'docling' || '' }}
|
||||
INSTALL_TESSERACT=${{ matrix.variant == '-docling' && 'true' || 'false' }}
|
||||
tags: |
|
||||
${{ secrets.DOCKER_USERNAME }}/docsgpt:${{ github.event.release.tag_name }}-${{ matrix.suffix }}
|
||||
ghcr.io/${{ github.repository_owner }}/docsgpt:${{ github.event.release.tag_name }}-${{ matrix.suffix }}
|
||||
${{ secrets.DOCKER_USERNAME }}/docsgpt:${{ github.event.release.tag_name }}${{ matrix.variant }}-${{ matrix.platform == 'linux/arm64' && 'arm64' || 'amd64' }}
|
||||
ghcr.io/${{ github.repository_owner }}/docsgpt:${{ github.event.release.tag_name }}${{ matrix.variant }}-${{ matrix.platform == 'linux/arm64' && 'arm64' || 'amd64' }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
provenance: false
|
||||
sbom: false
|
||||
cache-from: type=registry,ref=${{ secrets.DOCKER_USERNAME }}/docsgpt:latest
|
||||
cache-from: type=registry,ref=${{ secrets.DOCKER_USERNAME }}/docsgpt:latest${{ matrix.variant }}
|
||||
cache-to: type=inline
|
||||
|
||||
manifest:
|
||||
if: github.repository == 'arc53/DocsGPT'
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
variant: ["", "-docling"]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
packages: write
|
||||
@@ -87,26 +98,33 @@ jobs:
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Create and push manifest for DockerHub
|
||||
- name: Create and push multi-arch manifests
|
||||
env:
|
||||
TAG: ${{ github.event.release.tag_name }}${{ matrix.variant }}
|
||||
LATEST: latest${{ matrix.variant }}
|
||||
run: |
|
||||
set -e
|
||||
docker manifest create ${{ secrets.DOCKER_USERNAME }}/docsgpt:${{ github.event.release.tag_name }} \
|
||||
--amend ${{ secrets.DOCKER_USERNAME }}/docsgpt:${{ github.event.release.tag_name }}-amd64 \
|
||||
--amend ${{ secrets.DOCKER_USERNAME }}/docsgpt:${{ github.event.release.tag_name }}-arm64
|
||||
docker manifest push ${{ secrets.DOCKER_USERNAME }}/docsgpt:${{ github.event.release.tag_name }}
|
||||
docker manifest create ${{ secrets.DOCKER_USERNAME }}/docsgpt:latest \
|
||||
--amend ${{ secrets.DOCKER_USERNAME }}/docsgpt:${{ github.event.release.tag_name }}-amd64 \
|
||||
--amend ${{ secrets.DOCKER_USERNAME }}/docsgpt:${{ github.event.release.tag_name }}-arm64
|
||||
docker manifest push ${{ secrets.DOCKER_USERNAME }}/docsgpt:latest
|
||||
for repo in "${{ secrets.DOCKER_USERNAME }}/docsgpt" "ghcr.io/${{ github.repository_owner }}/docsgpt"; do
|
||||
for name in "$TAG" "$LATEST"; do
|
||||
docker manifest create "$repo:$name" \
|
||||
--amend "$repo:$TAG-amd64" \
|
||||
--amend "$repo:$TAG-arm64"
|
||||
docker manifest push "$repo:$name"
|
||||
done
|
||||
done
|
||||
|
||||
- name: Create and push manifest for ghcr.io
|
||||
release-assets:
|
||||
if: github.repository == 'arc53/DocsGPT'
|
||||
needs: manifest
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Attach the standalone compose file to the release
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
set -e
|
||||
docker manifest create ghcr.io/${{ github.repository_owner }}/docsgpt:${{ github.event.release.tag_name }} \
|
||||
--amend ghcr.io/${{ github.repository_owner }}/docsgpt:${{ github.event.release.tag_name }}-amd64 \
|
||||
--amend ghcr.io/${{ github.repository_owner }}/docsgpt:${{ github.event.release.tag_name }}-arm64
|
||||
docker manifest push ghcr.io/${{ github.repository_owner }}/docsgpt:${{ github.event.release.tag_name }}
|
||||
docker manifest create ghcr.io/${{ github.repository_owner }}/docsgpt:latest \
|
||||
--amend ghcr.io/${{ github.repository_owner }}/docsgpt:${{ github.event.release.tag_name }}-amd64 \
|
||||
--amend ghcr.io/${{ github.repository_owner }}/docsgpt:${{ github.event.release.tag_name }}-arm64
|
||||
docker manifest push ghcr.io/${{ github.repository_owner }}/docsgpt:latest
|
||||
gh release upload "${{ github.event.release.tag_name }}" \
|
||||
deployment/docker-compose-standalone.yaml --clobber
|
||||
@@ -11,14 +11,11 @@ jobs:
|
||||
if: github.repository == 'arc53/DocsGPT'
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- platform: linux/amd64
|
||||
runner: ubuntu-latest
|
||||
suffix: amd64
|
||||
- platform: linux/arm64
|
||||
runner: ubuntu-24.04-arm
|
||||
suffix: arm64
|
||||
runs-on: ${{ matrix.runner }}
|
||||
platform: [linux/amd64, linux/arm64]
|
||||
# "" is the slim default image; "-docling" bakes the docling parser
|
||||
# engine, its models and tesseract in (OCR-ready).
|
||||
variant: ["", "-docling"]
|
||||
runs-on: ${{ matrix.platform == 'linux/arm64' && 'ubuntu-24.04-arm' || 'ubuntu-latest' }}
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
@@ -36,7 +33,7 @@ jobs:
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
|
||||
- name: Login to ghcr.io
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
@@ -44,6 +41,17 @@ jobs:
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Image metadata (OCI labels)
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: |
|
||||
${{ secrets.DOCKER_USERNAME }}/docsgpt
|
||||
ghcr.io/${{ github.repository_owner }}/docsgpt
|
||||
labels: |
|
||||
org.opencontainers.image.title=DocsGPT${{ matrix.variant }}
|
||||
org.opencontainers.image.version=develop
|
||||
|
||||
- name: Build and push platform-specific images
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
@@ -51,17 +59,24 @@ jobs:
|
||||
platforms: ${{ matrix.platform }}
|
||||
context: ./application
|
||||
push: true
|
||||
build-args: |
|
||||
EXTRAS=${{ matrix.variant == '-docling' && 'docling' || '' }}
|
||||
INSTALL_TESSERACT=${{ matrix.variant == '-docling' && 'true' || 'false' }}
|
||||
tags: |
|
||||
${{ secrets.DOCKER_USERNAME }}/docsgpt:develop-${{ matrix.suffix }}
|
||||
ghcr.io/${{ github.repository_owner }}/docsgpt:develop-${{ matrix.suffix }}
|
||||
${{ secrets.DOCKER_USERNAME }}/docsgpt:develop${{ matrix.variant }}-${{ matrix.platform == 'linux/arm64' && 'arm64' || 'amd64' }}
|
||||
ghcr.io/${{ github.repository_owner }}/docsgpt:develop${{ matrix.variant }}-${{ matrix.platform == 'linux/arm64' && 'arm64' || 'amd64' }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
provenance: false
|
||||
sbom: false
|
||||
cache-from: type=registry,ref=${{ secrets.DOCKER_USERNAME }}/docsgpt:develop
|
||||
cache-from: type=registry,ref=${{ secrets.DOCKER_USERNAME }}/docsgpt:develop${{ matrix.variant }}
|
||||
cache-to: type=inline
|
||||
|
||||
manifest:
|
||||
if: github.repository == 'arc53/DocsGPT'
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
variant: ["", "-docling"]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
packages: write
|
||||
@@ -77,24 +92,22 @@ jobs:
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
|
||||
- name: Login to ghcr.io
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Create and push manifest for DockerHub
|
||||
run: |
|
||||
docker manifest create ${{ secrets.DOCKER_USERNAME }}/docsgpt:develop \
|
||||
--amend ${{ secrets.DOCKER_USERNAME }}/docsgpt:develop-amd64 \
|
||||
--amend ${{ secrets.DOCKER_USERNAME }}/docsgpt:develop-arm64
|
||||
docker manifest push ${{ secrets.DOCKER_USERNAME }}/docsgpt:develop
|
||||
|
||||
- name: Create and push manifest for ghcr.io
|
||||
- name: Create and push multi-arch manifests
|
||||
env:
|
||||
TAG: develop${{ matrix.variant }}
|
||||
run: |
|
||||
docker manifest create ghcr.io/${{ github.repository_owner }}/docsgpt:develop \
|
||||
--amend ghcr.io/${{ github.repository_owner }}/docsgpt:develop-amd64 \
|
||||
--amend ghcr.io/${{ github.repository_owner }}/docsgpt:develop-arm64
|
||||
docker manifest push ghcr.io/${{ github.repository_owner }}/docsgpt:develop
|
||||
set -e
|
||||
for repo in "${{ secrets.DOCKER_USERNAME }}/docsgpt" "ghcr.io/${{ github.repository_owner }}/docsgpt"; do
|
||||
docker manifest create "$repo:$TAG" \
|
||||
--amend "$repo:$TAG-amd64" \
|
||||
--amend "$repo:$TAG-arm64"
|
||||
docker manifest push "$repo:$TAG"
|
||||
done
|
||||
@@ -0,0 +1,52 @@
|
||||
name: Verify the Docker image works offline
|
||||
|
||||
# Builds the backend image and runs its offline check with networking off, so
|
||||
# a change that reintroduces a first-request download (a tokenizer, tiktoken's
|
||||
# encoding, an embedding model) fails here instead of in an air-gapped install.
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'application/Dockerfile'
|
||||
- 'application/.dockerignore'
|
||||
- 'application/requirements*.txt'
|
||||
- 'application/scripts/prefetch_models.py'
|
||||
- 'application/scripts/verify_offline.py'
|
||||
- 'application/vectorstore/model_registry.py'
|
||||
- 'application/parser/tokenization.py'
|
||||
- 'application/vectorstore/embeddings_local.py'
|
||||
- '.github/workflows/docker-image-verify.yml'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
verify:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Build the slim image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
file: ./application/Dockerfile
|
||||
context: ./application
|
||||
platforms: linux/amd64
|
||||
load: true
|
||||
tags: docsgpt:verify
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
- name: Image size
|
||||
run: |
|
||||
docker image inspect docsgpt:verify --format '{{.Size}}' | awk '{printf "uncompressed: %.2f GB\n", $1/1e9}'
|
||||
docker history docsgpt:verify --format '{{.Size}}\t{{.CreatedBy}}' | head -20
|
||||
|
||||
- name: Offline verification (no network)
|
||||
run: |
|
||||
docker run --rm --network none docsgpt:verify \
|
||||
python -m application.scripts.verify_offline
|
||||
@@ -20,3 +20,18 @@ jobs:
|
||||
uses: chartboost/ruff-action@v1
|
||||
with:
|
||||
version: 0.14.10
|
||||
|
||||
requirements-in-sync:
|
||||
# application/requirements*.txt are exported from uv.lock; fail when a
|
||||
# change to pyproject.toml or uv.lock was not re-exported.
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: astral-sh/setup-uv@v6
|
||||
|
||||
- name: Re-export and diff
|
||||
run: |
|
||||
uv lock --check
|
||||
bash scripts/export_requirements.sh
|
||||
git diff --exit-code -- application/requirements.txt application/requirements-docling.txt application/requirements-milvus.txt
|
||||
@@ -0,0 +1,23 @@
|
||||
# Build context is application/. Keep local state and caches out of the image.
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
.pytest_cache/
|
||||
.ruff_cache/
|
||||
.coverage
|
||||
htmlcov/
|
||||
*.log
|
||||
|
||||
# Runtime data: bind-mounted or created at run time, never baked in.
|
||||
indexes/
|
||||
inputs/
|
||||
vectors/
|
||||
*.faiss
|
||||
*.pkl
|
||||
|
||||
# Secrets and local config.
|
||||
.env
|
||||
.env.*
|
||||
|
||||
# Not needed inside the image.
|
||||
Dockerfile
|
||||
.dockerignore
|
||||
+112
-96
@@ -1,74 +1,81 @@
|
||||
# Builder Stage
|
||||
FROM ubuntu:24.04 as builder
|
||||
# DocsGPT backend image.
|
||||
#
|
||||
# Build args:
|
||||
# EXTRAS comma-separated optional extras to bake in, matching the
|
||||
# pyproject extras / requirements-<extra>.txt files:
|
||||
# docling (layout-model parser + OCR backend), milvus.
|
||||
# INSTALL_DOCLING legacy alias for EXTRAS=docling (setup.sh writes it).
|
||||
# INSTALL_TESSERACT bake the tesseract binary for OCR_ENGINE=tesseract.
|
||||
# EMBEDDINGS_PREFETCH registry names of the embedding models to bake; empty
|
||||
# bakes both defaults (mpnet for upgrades, granite for
|
||||
# new installs).
|
||||
#
|
||||
# Everything the default configuration needs is inside the image: embedding
|
||||
# models, their tokenizers, tiktoken's encoding and, with the docling extra,
|
||||
# docling's layout/table/OCR models. `python -m application.scripts.verify_offline`
|
||||
# under `docker run --network none` proves it.
|
||||
|
||||
FROM ubuntu:24.04 AS builder
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Ubuntu 24.04 ships Python 3.12 in its main archive: no PPA needed. Every pin
|
||||
# resolves to a wheel, so no compiler toolchain either.
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends python3.12 python3.12-venv ca-certificates && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY requirements.txt requirements-docling.txt requirements-milvus.txt ./
|
||||
|
||||
RUN python3.12 -m venv /venv
|
||||
ENV PATH="/venv/bin:$PATH"
|
||||
|
||||
RUN pip install --no-cache-dir --upgrade pip && \
|
||||
pip install --no-cache-dir --only-binary=:all: -r requirements.txt
|
||||
|
||||
# Optional extras. Each requirements-<extra>.txt is exported from the same
|
||||
# lock as requirements.txt, so installing it on top only adds the extra's
|
||||
# packages. The docling file takes torch from the CPU-only PyTorch index.
|
||||
# Not wheels-only: docling's antlr4 runtime ships as a pure-Python sdist.
|
||||
ARG EXTRAS=""
|
||||
ARG INSTALL_DOCLING=false
|
||||
RUN set -e; \
|
||||
extras="$EXTRAS"; \
|
||||
if [ "$INSTALL_DOCLING" = "true" ]; then extras="$extras,docling"; fi; \
|
||||
for extra in $(echo "$extras" | tr ',' ' '); do \
|
||||
echo "Installing extra: $extra"; \
|
||||
pip install --no-cache-dir -r "requirements-$extra.txt"; \
|
||||
done
|
||||
|
||||
# google-api-python-client bundles discovery documents for ~600 Google APIs
|
||||
# (99 MB). The application builds one client, Drive v3; keep only its document.
|
||||
# Building another API's client needs its file back, or static_discovery=False.
|
||||
RUN find /venv/lib/python3.12/site-packages/googleapiclient/discovery_cache/documents \
|
||||
-type f ! -name 'drive.v3.json' -delete
|
||||
|
||||
|
||||
FROM ubuntu:24.04 AS final
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y software-properties-common && \
|
||||
add-apt-repository ppa:deadsnakes/ppa && \
|
||||
apt-get update && \
|
||||
apt-get install -y --no-install-recommends gcc g++ wget unzip libc6-dev python3.12 python3.12-venv python3.12-dev && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Verify Python installation and setup symlink
|
||||
RUN if [ -f /usr/bin/python3.12 ]; then \
|
||||
ln -s /usr/bin/python3.12 /usr/bin/python; \
|
||||
else \
|
||||
echo "Python 3.12 not found"; exit 1; \
|
||||
fi
|
||||
|
||||
# Install Rust
|
||||
RUN wget -q -O - https://sh.rustup.rs | sh -s -- -y
|
||||
|
||||
# Clean up to reduce container size
|
||||
RUN apt-get remove --purge -y wget unzip && apt-get autoremove -y && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Copy requirements manifests
|
||||
COPY requirements.txt requirements-docling.txt ./
|
||||
|
||||
# Setup Python virtual environment
|
||||
RUN python3.12 -m venv /venv
|
||||
|
||||
# Activate virtual environment and install Python packages
|
||||
ENV PATH="/venv/bin:$PATH"
|
||||
|
||||
# Install Python packages
|
||||
RUN pip install --no-cache-dir --upgrade pip && \
|
||||
pip install --no-cache-dir tiktoken && \
|
||||
pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
# Optional docling parser engine (DOC_PARSER_ENGINE=docling, the docling OCR
|
||||
# backend, read_document's structured output) — OFF by default: it pulls the
|
||||
# layout/OCR model stack and adds gigabytes to the image. anydoc (in
|
||||
# requirements.txt) is the default parser and needs none of it, and OCR runs
|
||||
# natively on tesseract (below, also opt-in) without it.
|
||||
ARG INSTALL_DOCLING=false
|
||||
RUN if [ "$INSTALL_DOCLING" = "true" ]; then \
|
||||
pip install --no-cache-dir -r requirements-docling.txt; \
|
||||
fi
|
||||
|
||||
# Final Stage
|
||||
FROM ubuntu:24.04 as final
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y software-properties-common && \
|
||||
add-apt-repository ppa:deadsnakes/ppa && \
|
||||
apt-get update && apt-get install -y --no-install-recommends \
|
||||
python3.12 \
|
||||
libgl1 \
|
||||
libglib2.0-0 \
|
||||
poppler-utils \
|
||||
&& \
|
||||
apt-get install -y --no-install-recommends python3.12 poppler-utils ca-certificates && \
|
||||
ln -s /usr/bin/python3.12 /usr/bin/python && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# opencv (rapidocr, part of the docling extra) needs libGL at import time.
|
||||
ARG EXTRAS=""
|
||||
ARG INSTALL_DOCLING=false
|
||||
RUN if [ "$INSTALL_DOCLING" = "true" ] || echo ",$EXTRAS," | grep -q ",docling,"; then \
|
||||
apt-get update && \
|
||||
apt-get install -y --no-install-recommends libgl1 libglib2.0-0 && \
|
||||
rm -rf /var/lib/apt/lists/*; \
|
||||
fi
|
||||
|
||||
# Optional tesseract OCR engine (OCR_ENABLED=true with OCR_ENGINE=tesseract,
|
||||
# the default engine) — OFF by default like every other OCR dependency; OCR
|
||||
# itself is off unless configured. Opt in with --build-arg
|
||||
# INSTALL_TESSERACT=true (setup.sh writes it to .env when OCR is enabled);
|
||||
# ~35 MB of system packages. Extra language packs are a deployment concern
|
||||
# (apt: tesseract-ocr-<lang>, then list them in OCR_LANGS). A DeepSeek-OCR
|
||||
# endpoint (OCR_ENGINE=deepseek) needs none of this.
|
||||
# the default engine); ~35 MB of system packages. Extra language packs are a
|
||||
# deployment concern (apt: tesseract-ocr-<lang>, then list them in OCR_LANGS).
|
||||
# A DeepSeek-OCR endpoint (OCR_ENGINE=deepseek) needs none of this.
|
||||
ARG INSTALL_TESSERACT=false
|
||||
RUN if [ "$INSTALL_TESSERACT" = "true" ]; then \
|
||||
apt-get update && \
|
||||
@@ -76,61 +83,70 @@ RUN if [ "$INSTALL_TESSERACT" = "true" ]; then \
|
||||
rm -rf /var/lib/apt/lists/*; \
|
||||
fi
|
||||
|
||||
# Set working directory
|
||||
LABEL org.opencontainers.image.source="https://github.com/arc53/DocsGPT" \
|
||||
org.opencontainers.image.title="DocsGPT" \
|
||||
org.opencontainers.image.description="DocsGPT backend: API and Celery worker" \
|
||||
org.opencontainers.image.licenses="MIT"
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Create a non-root user: `appuser` (Feel free to choose a name)
|
||||
# The process user owns /app so the model prefetch below can run as it: an
|
||||
# unprivileged prefetch writes the model files with the right owner up front,
|
||||
# instead of a trailing chown -R that rewrites every model file into a second
|
||||
# layer.
|
||||
RUN groupadd -r appuser && \
|
||||
useradd -r -g appuser -d /app -s /sbin/nologin -c "Docker image user" appuser
|
||||
useradd -r -g appuser -d /app -s /sbin/nologin -c "Docker image user" appuser && \
|
||||
chown appuser:appuser /app && \
|
||||
install -d -o appuser -g appuser /app/models /app/application
|
||||
|
||||
# Copy the virtual environment and model from the builder stage
|
||||
COPY --from=builder /venv /venv
|
||||
|
||||
# Pre-fetch the embedding models into FastEmbed's cache so a fresh container
|
||||
# does not download on first ingest and an air-gapped install works at all.
|
||||
# Both defaults are baked: an upgraded deployment keeps using mpnet until it
|
||||
# runs the re-embed script, while a new one starts on granite.
|
||||
# The prefetch writes hub-layout snapshots (including tokenizer.json) here, so
|
||||
# HF_HUB_CACHE has to point at the same directory: chunking loads the tokenizer
|
||||
# through ``tokenizers``, which reads the hub cache and would otherwise fetch
|
||||
# over the network on first ingest -- and fall back to cl100k when offline.
|
||||
# Every cache the application reads at run time lives under /app/models and is
|
||||
# filled at build time:
|
||||
# EMBEDDINGS_CACHE_DIR / HF_HUB_CACHE FastEmbed models and their tokenizers
|
||||
# (chunking reads tokenizer.json from
|
||||
# the same hub-layout snapshot)
|
||||
# TIKTOKEN_CACHE_DIR cl100k_base for token accounting
|
||||
# DOCLING_ARTIFACTS_PATH docling's models (docling extra only)
|
||||
ENV EMBEDDINGS_CACHE_DIR=/app/models \
|
||||
HF_HUB_CACHE=/app/models
|
||||
HF_HUB_CACHE=/app/models \
|
||||
TIKTOKEN_CACHE_DIR=/app/models/tiktoken \
|
||||
DOCLING_ARTIFACTS_PATH=/app/models/docling \
|
||||
HF_HUB_DISABLE_TELEMETRY=1 \
|
||||
PATH="/venv/bin:$PATH"
|
||||
|
||||
# Only the modules the prefetch imports are copied first, so an unrelated
|
||||
# source edit does not invalidate the model layer.
|
||||
COPY --chown=appuser:appuser __init__.py /app/application/__init__.py
|
||||
COPY --chown=appuser:appuser scripts/__init__.py scripts/prefetch_models.py /app/application/scripts/
|
||||
COPY --chown=appuser:appuser vectorstore/__init__.py vectorstore/model_registry.py /app/application/vectorstore/
|
||||
|
||||
USER appuser
|
||||
|
||||
# Only the modules the prefetch imports are copied first. It reaches nothing
|
||||
# beyond model_registry, which is stdlib-only, so keeping the full source copy
|
||||
# below this layer stops an unrelated edit from re-downloading ~780 MB of model
|
||||
# artifacts on every build.
|
||||
COPY __init__.py /app/application/__init__.py
|
||||
COPY scripts/__init__.py scripts/prefetch_models.py /app/application/scripts/
|
||||
COPY vectorstore/__init__.py vectorstore/model_registry.py /app/application/vectorstore/
|
||||
ARG EMBEDDINGS_PREFETCH=""
|
||||
RUN PYTHONPATH=/app /venv/bin/python -m application.scripts.prefetch_models ${EMBEDDINGS_PREFETCH}
|
||||
RUN PYTHONPATH=/app python -m application.scripts.prefetch_models ${EMBEDDINGS_PREFETCH} && \
|
||||
rm -rf /app/models/.locks /app/.cache
|
||||
|
||||
# Copy your application code
|
||||
COPY . /app/application
|
||||
# docling downloads its layout, table-structure and OCR models on first parse;
|
||||
# bake them so the docling variant is as self-contained as the default image.
|
||||
RUN if python -c "import docling" 2>/dev/null; then \
|
||||
docling-tools models download --output-dir /app/models/docling layout tableformer rapidocr && \
|
||||
rm -rf /app/.cache; \
|
||||
fi
|
||||
|
||||
# Change the ownership of the /app directory to the appuser
|
||||
COPY --chown=appuser:appuser . /app/application
|
||||
|
||||
RUN mkdir -p /app/application/inputs/local
|
||||
RUN chown -R appuser:appuser /app
|
||||
|
||||
# Set environment variables
|
||||
ENV FLASK_APP=app.py \
|
||||
FLASK_DEBUG=true \
|
||||
PATH="/venv/bin:$PATH"
|
||||
ENV FLASK_APP=app.py
|
||||
|
||||
ENV MALLOC_ARENA_MAX=2 \
|
||||
OMP_NUM_THREADS=4 \
|
||||
MKL_NUM_THREADS=4 \
|
||||
OPENBLAS_NUM_THREADS=4
|
||||
|
||||
# Expose the port the app runs on
|
||||
EXPOSE 7091
|
||||
|
||||
# Switch to non-root user
|
||||
USER appuser
|
||||
|
||||
# BoundedDrainUvicornWorker makes max_requests recycles safe with held-open SSE
|
||||
# connections (see application/gunicorn_worker.py); with recycles now safe,
|
||||
# --max-requests is raised (kept for memory hygiene) to cut churn.
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
services:
|
||||
frontend:
|
||||
build: ../frontend
|
||||
build:
|
||||
context: ../frontend
|
||||
target: dev
|
||||
environment:
|
||||
- VITE_API_HOST=http://localhost:7091
|
||||
- VITE_API_STREAMING=$VITE_API_STREAMING
|
||||
@@ -13,6 +15,7 @@ services:
|
||||
build:
|
||||
context: ../application
|
||||
args:
|
||||
EXTRAS: ${EXTRAS:-}
|
||||
INSTALL_DOCLING: ${INSTALL_DOCLING:-false}
|
||||
# Off by default; deployments running OCR_ENABLED=true with tesseract
|
||||
# must set INSTALL_TESSERACT=true before rebuilding (see docker-compose.yaml).
|
||||
@@ -40,6 +43,7 @@ services:
|
||||
build:
|
||||
context: ../application
|
||||
args:
|
||||
EXTRAS: ${EXTRAS:-}
|
||||
INSTALL_DOCLING: ${INSTALL_DOCLING:-false}
|
||||
# Off by default; deployments running OCR_ENABLED=true with tesseract
|
||||
# must set INSTALL_TESSERACT=true before rebuilding (see docker-compose.yaml).
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
# Pre-built images from Docker Hub (mirrored at ghcr.io/arc53).
|
||||
# DOCSGPT_IMAGE_TAG develop (default, follows main) or a release, e.g. 0.20.0
|
||||
# DOCSGPT_IMAGE_VARIANT empty (default, slim) or -docling: docling parser engine,
|
||||
# its models, and tesseract baked in (OCR-ready)
|
||||
# Set them in ../.env or the shell. deployment/docker-compose-standalone.yaml is
|
||||
# the same stack without a git checkout.
|
||||
name: docsgpt-oss
|
||||
services:
|
||||
|
||||
frontend:
|
||||
image: arc53/docsgpt-fe:develop
|
||||
image: arc53/docsgpt-fe:${DOCSGPT_IMAGE_TAG:-develop}
|
||||
environment:
|
||||
- VITE_API_HOST=http://localhost:7091
|
||||
- VITE_API_STREAMING=${VITE_API_STREAMING:-true}
|
||||
@@ -15,7 +21,7 @@ services:
|
||||
|
||||
backend:
|
||||
user: root
|
||||
image: arc53/docsgpt:develop
|
||||
image: arc53/docsgpt:${DOCSGPT_IMAGE_TAG:-develop}${DOCSGPT_IMAGE_VARIANT:-}
|
||||
env_file:
|
||||
- ../.env
|
||||
environment:
|
||||
@@ -38,7 +44,7 @@ services:
|
||||
|
||||
worker:
|
||||
user: root
|
||||
image: arc53/docsgpt:develop
|
||||
image: arc53/docsgpt:${DOCSGPT_IMAGE_TAG:-develop}${DOCSGPT_IMAGE_VARIANT:-}
|
||||
# `parsing` queue carries read_document/parse_document; required for its await to resolve.
|
||||
command: celery -A application.app.celery worker -l INFO -B -Q docsgpt,parsing,embeddings
|
||||
env_file:
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
# DocsGPT from pre-built images, with no git checkout.
|
||||
#
|
||||
# curl -fsSLO https://raw.githubusercontent.com/arc53/DocsGPT/main/deployment/docker-compose-standalone.yaml
|
||||
# printf 'LLM_PROVIDER=docsgpt\nVITE_API_STREAMING=true\n' > .env # or any provider, see the settings guide
|
||||
# docker compose up -d
|
||||
# open http://localhost:5173
|
||||
#
|
||||
# Every release also attaches this file as an asset. Settings come from .env
|
||||
# next to this file (any DocsGPT setting; the compose-internal service URLs
|
||||
# below take precedence). Data lives in named volumes, so `docker compose
|
||||
# down` keeps it and `docker compose down -v` removes it.
|
||||
#
|
||||
# DOCSGPT_IMAGE_TAG release to run, e.g. 0.20.0 (default: latest release);
|
||||
# develop follows the main branch
|
||||
# DOCSGPT_IMAGE_VARIANT empty (slim, default) or -docling: docling parser
|
||||
# engine, its models, and tesseract baked in (OCR-ready)
|
||||
name: docsgpt
|
||||
|
||||
services:
|
||||
frontend:
|
||||
image: arc53/docsgpt-fe:${DOCSGPT_IMAGE_TAG:-latest}
|
||||
env_file:
|
||||
- path: .env
|
||||
required: false
|
||||
environment:
|
||||
- VITE_API_HOST=${VITE_API_HOST:-http://localhost:7091}
|
||||
- VITE_API_STREAMING=${VITE_API_STREAMING:-true}
|
||||
ports:
|
||||
- "5173:5173"
|
||||
depends_on:
|
||||
- backend
|
||||
|
||||
backend:
|
||||
image: arc53/docsgpt:${DOCSGPT_IMAGE_TAG:-latest}${DOCSGPT_IMAGE_VARIANT:-}
|
||||
env_file:
|
||||
- path: .env
|
||||
required: false
|
||||
environment:
|
||||
- CELERY_BROKER_URL=redis://redis:6379/0
|
||||
- CELERY_RESULT_BACKEND=redis://redis:6379/1
|
||||
- CACHE_REDIS_URL=redis://redis:6379/2
|
||||
- POSTGRES_URI=postgresql://docsgpt:docsgpt@postgres:5432/docsgpt
|
||||
ports:
|
||||
- "7091:7091"
|
||||
volumes:
|
||||
- indexes:/app/indexes
|
||||
- inputs:/app/inputs
|
||||
- vectors:/app/vectors
|
||||
depends_on:
|
||||
redis:
|
||||
condition: service_started
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
restart: unless-stopped
|
||||
|
||||
worker:
|
||||
image: arc53/docsgpt:${DOCSGPT_IMAGE_TAG:-latest}${DOCSGPT_IMAGE_VARIANT:-}
|
||||
# Consumes the default queue plus `parsing` (read_document) and `embeddings`
|
||||
# (query embedding); without the latter every search times out.
|
||||
command: celery -A application.app.celery worker -l INFO -B -Q docsgpt,parsing,embeddings
|
||||
env_file:
|
||||
- path: .env
|
||||
required: false
|
||||
environment:
|
||||
- CELERY_BROKER_URL=redis://redis:6379/0
|
||||
- CELERY_RESULT_BACKEND=redis://redis:6379/1
|
||||
- CACHE_REDIS_URL=redis://redis:6379/2
|
||||
- POSTGRES_URI=postgresql://docsgpt:docsgpt@postgres:5432/docsgpt
|
||||
- API_URL=http://backend:7091
|
||||
volumes:
|
||||
- indexes:/app/indexes
|
||||
- inputs:/app/inputs
|
||||
- vectors:/app/vectors
|
||||
depends_on:
|
||||
redis:
|
||||
condition: service_started
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
restart: unless-stopped
|
||||
|
||||
redis:
|
||||
image: redis:6-alpine
|
||||
restart: unless-stopped
|
||||
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
environment:
|
||||
- POSTGRES_USER=docsgpt
|
||||
- POSTGRES_PASSWORD=docsgpt
|
||||
- POSTGRES_DB=docsgpt
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U docsgpt -d docsgpt"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
indexes:
|
||||
inputs:
|
||||
vectors:
|
||||
postgres_data:
|
||||
@@ -1,7 +1,11 @@
|
||||
name: docsgpt-oss
|
||||
services:
|
||||
frontend:
|
||||
build: ../frontend
|
||||
build:
|
||||
context: ../frontend
|
||||
# Vite dev server with hot reload over the bind mount below. The default
|
||||
# target (what Docker Hub publishes) is a static build behind nginx.
|
||||
target: dev
|
||||
volumes:
|
||||
- ../frontend/src:/app/src
|
||||
environment:
|
||||
@@ -18,8 +22,10 @@ services:
|
||||
build:
|
||||
context: ../application
|
||||
args:
|
||||
# Bake the optional docling engine (layout-model OCR backend, structured
|
||||
# output) into the image: set INSTALL_DOCLING=true in ../.env or the shell.
|
||||
# Optional extras to bake in (comma-separated): docling, milvus. The
|
||||
# docling extra brings the layout-model parser/OCR backend and its
|
||||
# models. INSTALL_DOCLING=true is the older spelling of EXTRAS=docling.
|
||||
EXTRAS: ${EXTRAS:-}
|
||||
INSTALL_DOCLING: ${INSTALL_DOCLING:-false}
|
||||
# Bake the tesseract binary behind OCR_ENABLED=true (~35 MB): set
|
||||
# INSTALL_TESSERACT=true in ../.env or the shell (setup.sh does this
|
||||
@@ -53,6 +59,7 @@ services:
|
||||
build:
|
||||
context: ../application
|
||||
args:
|
||||
EXTRAS: ${EXTRAS:-}
|
||||
INSTALL_DOCLING: ${INSTALL_DOCLING:-false}
|
||||
INSTALL_TESSERACT: ${INSTALL_TESSERACT:-false}
|
||||
# Consumes the default queue AND the dedicated `parsing` (read_document /
|
||||
|
||||
@@ -17,9 +17,57 @@ Docker is the recommended method for deploying DocsGPT, providing a consistent a
|
||||
|
||||
**Important Note for Windows Users:** Docker Desktop on Windows generally requires the WSL 2 backend to function correctly, especially when using features like host networking which are utilized in DocsGPT's Docker Compose setup. Ensure WSL 2 is enabled and configured in Docker Desktop settings.
|
||||
|
||||
## Quickest Setup: Using DocsGPT Public API
|
||||
## Quickest Setup: Pre-built Images, No Checkout
|
||||
|
||||
The fastest way to try out DocsGPT is by using the public API endpoint. This requires minimal configuration and no local LLM setup.
|
||||
Every release publishes ready-to-run images to Docker Hub (`arc53/docsgpt`,
|
||||
`arc53/docsgpt-fe`) and GitHub Container Registry (`ghcr.io/arc53/docsgpt`,
|
||||
`ghcr.io/arc53/docsgpt-fe`) for `linux/amd64` and `linux/arm64`. The images
|
||||
contain everything the default configuration needs (embedding models,
|
||||
tokenizers, tiktoken's encoding), so a fresh container makes no downloads on
|
||||
first use. You do not need the source tree to run them:
|
||||
|
||||
1. **Download the standalone Compose file** (also attached to every
|
||||
[release](https://github.com/arc53/DocsGPT/releases)):
|
||||
|
||||
```bash
|
||||
mkdir docsgpt && cd docsgpt
|
||||
curl -fsSLO https://raw.githubusercontent.com/arc53/DocsGPT/main/deployment/docker-compose-standalone.yaml
|
||||
```
|
||||
|
||||
2. **Create a `.env` next to it** with your settings, for example the public API:
|
||||
|
||||
```
|
||||
LLM_PROVIDER=docsgpt
|
||||
VITE_API_STREAMING=true
|
||||
```
|
||||
|
||||
3. **Start it:**
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose-standalone.yaml up -d
|
||||
```
|
||||
|
||||
Then open [http://localhost:5173/](http://localhost:5173/). Data lives in
|
||||
named Docker volumes; `docker compose -f docker-compose-standalone.yaml down`
|
||||
keeps it and `down -v` removes it.
|
||||
|
||||
**Tags and variants.** `DOCSGPT_IMAGE_TAG` picks the version: a release such
|
||||
as `0.20.0`, `latest` (the newest release, the default) or `develop` (follows
|
||||
the `main` branch). `DOCSGPT_IMAGE_VARIANT` picks the flavour: empty for the
|
||||
slim default image, or `-docling` for the image with the docling parser
|
||||
engine, its models and tesseract baked in (needed for OCR of scanned
|
||||
documents, see the [OCR guide](/Guides/ocr)). Both are read from `.env` or
|
||||
the shell, e.g. `DOCSGPT_IMAGE_TAG=0.20.0 DOCSGPT_IMAGE_VARIANT=-docling`.
|
||||
The same two variables drive `deployment/docker-compose-hub.yaml` in a
|
||||
checkout.
|
||||
|
||||
## Using the Source Checkout
|
||||
|
||||
With a clone of the repository, `deployment/docker-compose-hub.yaml` runs the
|
||||
same pre-built images while keeping your data in `application/indexes`,
|
||||
`application/inputs` and `application/vectors`, and `deployment/docker-compose.yaml`
|
||||
builds the images from your working tree (for local changes, or a build with
|
||||
extra packages: `EXTRAS=docling` in `.env`).
|
||||
|
||||
1. **Clone the DocsGPT Repository (if you haven't already):**
|
||||
|
||||
@@ -48,10 +96,12 @@ The fastest way to try out DocsGPT is by using the public API endpoint. This req
|
||||
Navigate to the root directory of the DocsGPT repository in your terminal and run:
|
||||
|
||||
```bash
|
||||
docker compose --env-file .env -f deployment/docker-compose.yaml up -d
|
||||
docker compose --env-file .env -f deployment/docker-compose-hub.yaml up -d
|
||||
```
|
||||
|
||||
The `-d` flag runs Docker Compose in detached mode (in the background).
|
||||
To build the images from your working tree instead of pulling them, use
|
||||
`deployment/docker-compose.yaml` with `up --build -d`.
|
||||
|
||||
5. **Access DocsGPT in your browser:**
|
||||
|
||||
@@ -62,7 +112,7 @@ The fastest way to try out DocsGPT is by using the public API endpoint. This req
|
||||
To stop the application, navigate to the same directory in your terminal and run:
|
||||
|
||||
```bash
|
||||
docker compose -f deployment/docker-compose.yaml down
|
||||
docker compose -f deployment/docker-compose-hub.yaml down
|
||||
```
|
||||
|
||||
## Optional Ollama Setup (Local Models)
|
||||
@@ -84,11 +134,11 @@ There are two Ollama optional files:
|
||||
|
||||
**CPU:**
|
||||
```bash
|
||||
docker compose --env-file .env -f deployment/docker-compose.yaml -f deployment/optional/docker-compose.optional.ollama-cpu.yaml up -d
|
||||
docker compose --env-file .env -f deployment/docker-compose-hub.yaml -f deployment/optional/docker-compose.optional.ollama-cpu.yaml up -d
|
||||
```
|
||||
**GPU:**
|
||||
```bash
|
||||
docker compose --env-file .env -f deployment/docker-compose.yaml -f deployment/optional/docker-compose.optional.ollama-gpu.yaml up -d
|
||||
docker compose --env-file .env -f deployment/docker-compose-hub.yaml -f deployment/optional/docker-compose.optional.ollama-gpu.yaml up -d
|
||||
```
|
||||
|
||||
3. **Pull the Ollama Model:**
|
||||
@@ -96,11 +146,11 @@ There are two Ollama optional files:
|
||||
**Crucially, after launching with Ollama, you need to pull the desired model into the Ollama container.** Find the `LLM_NAME` you configured in your `.env` file (e.g., `llama3.2:1b`). Then execute the following command to pull the model *inside* the running Ollama container:
|
||||
|
||||
```bash
|
||||
docker compose -f deployment/docker-compose.yaml -f deployment/optional/docker-compose.optional.ollama-cpu.yaml exec -it ollama ollama pull <LLM_NAME>
|
||||
docker compose -f deployment/docker-compose-hub.yaml -f deployment/optional/docker-compose.optional.ollama-cpu.yaml exec -it ollama ollama pull <LLM_NAME>
|
||||
```
|
||||
or (for GPU):
|
||||
```bash
|
||||
docker compose -f deployment/docker-compose.yaml -f deployment/optional/docker-compose.optional.ollama-gpu.yaml exec -it ollama ollama pull <LLM_NAME>
|
||||
docker compose -f deployment/docker-compose-hub.yaml -f deployment/optional/docker-compose.optional.ollama-gpu.yaml exec -it ollama ollama pull <LLM_NAME>
|
||||
```
|
||||
Replace `<LLM_NAME>` with the actual model name from your `.env` file.
|
||||
|
||||
@@ -113,12 +163,12 @@ There are two Ollama optional files:
|
||||
To stop a DocsGPT setup launched with Ollama optional files, use `docker compose down` and include all the compose files used during the `up` command:
|
||||
|
||||
```bash
|
||||
docker compose -f deployment/docker-compose.yaml -f deployment/optional/docker-compose.optional.ollama-cpu.yaml down
|
||||
docker compose -f deployment/docker-compose-hub.yaml -f deployment/optional/docker-compose.optional.ollama-cpu.yaml down
|
||||
```
|
||||
or
|
||||
|
||||
```bash
|
||||
docker compose -f deployment/docker-compose.yaml -f deployment/optional/docker-compose.optional.ollama-gpu.yaml down
|
||||
docker compose -f deployment/docker-compose-hub.yaml -f deployment/optional/docker-compose.optional.ollama-gpu.yaml down
|
||||
```
|
||||
|
||||
**Important for GPU Usage:**
|
||||
|
||||
+25
-11
@@ -64,11 +64,17 @@ docker build --build-arg INSTALL_TESSERACT=true ./application
|
||||
`INSTALL_TESSERACT=true` in `.env` (or the shell) bakes tesseract plus the
|
||||
English pack into locally built backend and worker images; `setup.sh` writes
|
||||
it when you answer yes to the OCR question after choosing to build images
|
||||
locally. Pre-built Docker Hub images (`docker-compose-hub.yaml`) do not
|
||||
include it, so `setup.sh` leaves OCR at its default (off) for them; to OCR
|
||||
there, point `OCR_ENGINE=deepseek` at a DeepSeek-OCR endpoint or install
|
||||
`tesseract-ocr` in a derived image. With `OCR_ENABLED=true` and no binary on
|
||||
`PATH`, scanned pages fail with an install hint (text-layer documents are
|
||||
locally.
|
||||
|
||||
With pre-built images the switch is the image variant: every tag is
|
||||
published twice, slim (`arc53/docsgpt:<tag>`) and `-docling`
|
||||
(`arc53/docsgpt:<tag>-docling`), and the latter bakes tesseract, the docling
|
||||
engine and its models in. Set `DOCSGPT_IMAGE_VARIANT=-docling` in `.env` for
|
||||
`docker-compose-hub.yaml` or `docker-compose-standalone.yaml`; `setup.sh`
|
||||
writes it when you answer yes to the OCR question with Docker Hub images.
|
||||
Alternatively point `OCR_ENGINE=deepseek` at a DeepSeek-OCR endpoint, which
|
||||
needs no system package. With `OCR_ENABLED=true` and no binary on `PATH`,
|
||||
scanned pages fail with an install hint (text-layer documents are
|
||||
unaffected).
|
||||
|
||||
<Callout type="warning" emoji="⚠️">
|
||||
@@ -88,19 +94,27 @@ docling is not part of the base install, and OCR does not need it (see
|
||||
output:
|
||||
|
||||
```bash
|
||||
pip install -r application/requirements-docling.txt
|
||||
pip install -r application/requirements-docling.txt # or: uv sync --extra docling
|
||||
```
|
||||
|
||||
Docker images build without it by default; opt in with the build argument:
|
||||
That file is the core set plus the `docling` extra, exported from the same
|
||||
lock. On Linux it takes torch from the CPU-only PyTorch index, so the extra
|
||||
costs about 1.5 GB rather than the 2.7 GB the CUDA build of torch would; a
|
||||
GPU deployment can reinstall torch from PyPI on top.
|
||||
|
||||
Pre-built images: use the `-docling` variant (`arc53/docsgpt:<tag>-docling`,
|
||||
`DOCSGPT_IMAGE_VARIANT=-docling` in `.env`), which also bakes docling's
|
||||
layout, table-structure and RapidOCR models in so the first parse does not
|
||||
download them. Local builds opt in with the build argument:
|
||||
|
||||
```bash
|
||||
docker build --build-arg INSTALL_DOCLING=true ./application
|
||||
docker build --build-arg EXTRAS=docling ./application
|
||||
```
|
||||
|
||||
`deployment/docker-compose.yaml` forwards the same switch, so setting
|
||||
`INSTALL_DOCLING=true` in `.env` (or the shell) bakes docling into locally
|
||||
built backend and worker images; `setup.sh` offers it as a follow-up to the
|
||||
OCR question. Compose reads build arguments from the shell or from the
|
||||
`EXTRAS=docling` (or the older `INSTALL_DOCLING=true`) in `.env` (or the
|
||||
shell) bakes docling into locally built backend and worker images; `setup.sh`
|
||||
offers it as a follow-up to the OCR question. Compose reads build arguments from the shell or from the
|
||||
`.env` you pass with `--env-file .env` (not from the containers' `env_file`),
|
||||
so build with `docker compose --env-file .env -f deployment/docker-compose.yaml build`
|
||||
as `setup.sh` does. Pre-built Docker Hub images (`docker-compose-hub.yaml`)
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
node_modules/
|
||||
dist/
|
||||
.env
|
||||
.env.*
|
||||
Dockerfile
|
||||
.dockerignore
|
||||
*.log
|
||||
+45
-4
@@ -1,11 +1,52 @@
|
||||
FROM node:22-bullseye-slim
|
||||
# DocsGPT frontend image: a static production build served by nginx.
|
||||
#
|
||||
# Vite inlines VITE_* settings at build time, so the old image ran the Vite
|
||||
# dev server just to read VITE_API_HOST from the container environment. This
|
||||
# image builds once and injects the container's VITE_* variables at start-up
|
||||
# instead (docker/40-runtime-env.sh writes them to /config.js, which the app
|
||||
# reads before its own bundle; see src/env.ts). Same env vars, same port.
|
||||
#
|
||||
# Targets:
|
||||
# (default) nginx serving the built bundle -- what Docker Hub publishes
|
||||
# dev the Vite dev server with hot reload, for docker-compose.yaml's
|
||||
# bind-mounted frontend (build: target: dev)
|
||||
|
||||
FROM node:22-alpine AS deps
|
||||
|
||||
WORKDIR /app
|
||||
COPY package*.json ./
|
||||
RUN npm install
|
||||
RUN npm ci --no-audit --no-fund
|
||||
|
||||
|
||||
FROM deps AS dev
|
||||
|
||||
COPY . .
|
||||
|
||||
# vite.config.ts polls the filesystem when DOCKER is set: native fs events do
|
||||
# not cross a Windows host into a Linux container.
|
||||
ENV DOCKER=1
|
||||
EXPOSE 5173
|
||||
CMD ["npm", "run", "dev", "--", "--host"]
|
||||
|
||||
CMD [ "npm", "run", "dev", "--" , "--host"]
|
||||
|
||||
FROM deps AS build
|
||||
|
||||
COPY . .
|
||||
# Bake nothing deployment-specific; the runtime script supplies it.
|
||||
ENV VITE_API_HOST=""
|
||||
RUN npm run build && \
|
||||
sed -i 's|<head>|<head><script src="/config.js"></script>|' dist/index.html
|
||||
|
||||
|
||||
FROM nginx:1.27-alpine
|
||||
|
||||
LABEL org.opencontainers.image.source="https://github.com/arc53/DocsGPT" \
|
||||
org.opencontainers.image.title="DocsGPT frontend" \
|
||||
org.opencontainers.image.licenses="MIT"
|
||||
|
||||
COPY docker/nginx.conf /etc/nginx/conf.d/default.conf
|
||||
COPY docker/40-runtime-env.sh /docker-entrypoint.d/40-runtime-env.sh
|
||||
RUN chmod +x /docker-entrypoint.d/40-runtime-env.sh
|
||||
COPY --from=build /app/dist /usr/share/nginx/html
|
||||
|
||||
# Same port the dev server used, so compose files and docs keep working.
|
||||
EXPOSE 5173
|
||||
@@ -0,0 +1,23 @@
|
||||
#!/bin/sh
|
||||
# Expose the container's VITE_* environment to the static bundle.
|
||||
#
|
||||
# nginx's entrypoint runs everything in /docker-entrypoint.d before serving.
|
||||
# The generated /config.js is loaded by index.html ahead of the app bundle and
|
||||
# read by src/env.ts, so VITE_API_HOST and friends can differ per deployment
|
||||
# without rebuilding the image.
|
||||
set -eu
|
||||
|
||||
out=/usr/share/nginx/html/config.js
|
||||
{
|
||||
printf 'window.__DOCSGPT_ENV__ = {'
|
||||
first=1
|
||||
env | grep -E '^VITE_[A-Za-z0-9_]+=' | while IFS='=' read -r key value; do
|
||||
# JSON-escape backslashes and double quotes; values are plain URLs/ids.
|
||||
escaped=$(printf '%s' "$value" | sed 's/\\/\\\\/g; s/"/\\"/g')
|
||||
if [ "$first" -eq 1 ]; then first=0; else printf ','; fi
|
||||
printf '"%s":"%s"' "$key" "$escaped"
|
||||
done
|
||||
printf '};\n'
|
||||
} > "$out"
|
||||
|
||||
echo "runtime-env: wrote $(grep -o 'VITE_[A-Za-z0-9_]*' "$out" | wc -l | tr -d ' ') VITE_* values to /config.js"
|
||||
@@ -0,0 +1,25 @@
|
||||
server {
|
||||
listen 5173;
|
||||
server_name _;
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
gzip on;
|
||||
gzip_types text/plain text/css application/javascript application/json image/svg+xml;
|
||||
|
||||
# Hashed bundle assets are immutable; index.html and config.js are not.
|
||||
location /assets/ {
|
||||
add_header Cache-Control "public, max-age=31536000, immutable";
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
location = /config.js {
|
||||
add_header Cache-Control "no-store";
|
||||
}
|
||||
|
||||
# Single-page app: every unknown path renders index.html.
|
||||
location / {
|
||||
add_header Cache-Control "no-cache";
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import { envVar } from '@/env';
|
||||
import './locale/i18n';
|
||||
|
||||
import { useState } from 'react';
|
||||
@@ -108,8 +109,8 @@ export default function App() {
|
||||
const saved = localStorage.getItem('showNotification');
|
||||
return saved ? JSON.parse(saved) : true;
|
||||
});
|
||||
const notificationText = import.meta.env.VITE_NOTIFICATION_TEXT;
|
||||
const notificationLink = import.meta.env.VITE_NOTIFICATION_LINK;
|
||||
const notificationText = envVar('VITE_NOTIFICATION_TEXT');
|
||||
const notificationLink = envVar('VITE_NOTIFICATION_LINK');
|
||||
// Hide the changelog banner on public share routes — those pages are
|
||||
// embedded / shared externally and shouldn't carry product chrome.
|
||||
const isPublicShareRoute =
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { envVar } from '@/env';
|
||||
import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
|
||||
import {
|
||||
@@ -26,7 +27,7 @@ const initialState: ConversationState = {
|
||||
conversationId: null,
|
||||
};
|
||||
|
||||
const API_STREAMING = import.meta.env.VITE_API_STREAMING === 'true';
|
||||
const API_STREAMING = envVar('VITE_API_STREAMING') === 'true';
|
||||
|
||||
let abortController: AbortController | null = null;
|
||||
export function handlePreviewAbort() {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { envVar } from '@/env';
|
||||
import { withThrottle, type FetchLike } from './throttle';
|
||||
|
||||
export const baseURL =
|
||||
import.meta.env.VITE_API_HOST || 'https://docsapi.arc53.com';
|
||||
envVar('VITE_API_HOST') || 'https://docsapi.arc53.com';
|
||||
|
||||
const getHeaders = (
|
||||
token: string | null,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { envVar } from '@/env';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import drivePickerImport from 'react-google-drive-picker';
|
||||
@@ -121,9 +122,9 @@ const GoogleDrivePicker: React.FC<GoogleDrivePickerProps> = ({
|
||||
}
|
||||
|
||||
try {
|
||||
const clientId: string = import.meta.env.VITE_GOOGLE_CLIENT_ID;
|
||||
const clientId: string = envVar('VITE_GOOGLE_CLIENT_ID');
|
||||
const developerKey: string =
|
||||
import.meta.env.VITE_GOOGLE_PICKER_API_KEY ?? '';
|
||||
envVar('VITE_GOOGLE_PICKER_API_KEY') ?? '';
|
||||
|
||||
// Derive appId from clientId (extract numeric part before first dash)
|
||||
const appId = clientId ? clientId.split('-')[0] : null;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { envVar } from '@/env';
|
||||
import {
|
||||
useCallback,
|
||||
useEffect,
|
||||
@@ -63,7 +64,7 @@ const LIVE_TRANSCRIPTION_TIMESLICE_MS = 1000;
|
||||
const LIVE_CAPTURE_SAMPLE_RATE = 16000;
|
||||
const LIVE_CAPTURE_MAX_BUFFER_SECONDS = 20;
|
||||
const LIVE_SILENCE_RMS_THRESHOLD = 0.015;
|
||||
const ENABLE_VOICE_INPUT = import.meta.env.VITE_ENABLE_VOICE_INPUT === 'true';
|
||||
const ENABLE_VOICE_INPUT = envVar('VITE_ENABLE_VOICE_INPUT') === 'true';
|
||||
|
||||
type AudioContextWindow = Window &
|
||||
typeof globalThis & {
|
||||
@@ -526,7 +527,7 @@ export default function MessageInput({
|
||||
if (supported.length === 0) return;
|
||||
const files = supported;
|
||||
|
||||
const apiHost = import.meta.env.VITE_API_HOST;
|
||||
const apiHost = envVar('VITE_API_HOST');
|
||||
|
||||
if (files.length > 1) {
|
||||
const formData = new FormData();
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { envVar } from '@/env';
|
||||
import 'katex/dist/katex.min.css';
|
||||
|
||||
import { Pencil } from 'lucide-react';
|
||||
@@ -37,7 +38,7 @@ import ResearchProgress from './ResearchProgress';
|
||||
import { ToolCallsType } from './types';
|
||||
import { wikiWriteActionKey, wikiWritePath } from './wikiToolCall';
|
||||
|
||||
const DisableSourceFE = import.meta.env.VITE_DISABLE_SOURCE_FE || false;
|
||||
const DisableSourceFE = envVar('VITE_DISABLE_SOURCE_FE') || false;
|
||||
|
||||
const ConversationBubble = forwardRef<
|
||||
HTMLDivElement,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { envVar } from '@/env';
|
||||
import {
|
||||
createAsyncThunk,
|
||||
createListenerMiddleware,
|
||||
@@ -90,8 +91,8 @@ const initialState: ConversationState = {
|
||||
conversationId: null,
|
||||
};
|
||||
|
||||
const API_STREAMING = import.meta.env.VITE_API_STREAMING === 'true';
|
||||
const USE_V1_API = import.meta.env.VITE_USE_V1_API === 'true';
|
||||
const API_STREAMING = envVar('VITE_API_STREAMING') === 'true';
|
||||
const USE_V1_API = envVar('VITE_USE_V1_API') === 'true';
|
||||
|
||||
let abortController: AbortController | null = null;
|
||||
export function handleAbort() {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { envVar } from '@/env';
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
import type { PayloadAction } from '@reduxjs/toolkit';
|
||||
import store from '../store';
|
||||
@@ -12,7 +13,7 @@ import {
|
||||
clearAttachments,
|
||||
} from '../upload/uploadSlice';
|
||||
|
||||
const API_STREAMING = import.meta.env.VITE_API_STREAMING === 'true';
|
||||
const API_STREAMING = envVar('VITE_API_STREAMING') === 'true';
|
||||
interface SharedConversationsType {
|
||||
queries: Query[];
|
||||
apiKey?: string;
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Runtime-overridable build settings.
|
||||
*
|
||||
* Vite inlines `import.meta.env.VITE_*` at build time, which forced the
|
||||
* Docker image to run the dev server so `VITE_API_HOST` could change per
|
||||
* deployment. The production image serves a static build instead and writes
|
||||
* the container's `VITE_*` environment into `window.__DOCSGPT_ENV__` (see
|
||||
* frontend/docker/40-runtime-env.sh). That object wins over the build-time
|
||||
* value; outside Docker nothing sets it and the build-time value applies.
|
||||
*/
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
__DOCSGPT_ENV__?: Record<string, string | undefined>;
|
||||
}
|
||||
}
|
||||
|
||||
export function envVar(name: string): string {
|
||||
const runtime =
|
||||
typeof window !== 'undefined' ? window.__DOCSGPT_ENV__?.[name] : undefined;
|
||||
if (runtime !== undefined && runtime !== '') return runtime;
|
||||
const buildTime = (import.meta.env as Record<string, unknown>)[name];
|
||||
return typeof buildTime === 'string' ? buildTime : '';
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import { envVar } from '@/env';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useSelector } from 'react-redux';
|
||||
@@ -13,7 +14,7 @@ import { ActiveState } from '../models/misc';
|
||||
import { selectToken } from '../preferences/preferenceSlice';
|
||||
import ConfirmationModal from './ConfirmationModal';
|
||||
|
||||
const baseURL = import.meta.env.VITE_BASE_URL;
|
||||
const baseURL = envVar('VITE_BASE_URL');
|
||||
|
||||
type AgentDetailsModalProps = {
|
||||
agent: Agent;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { envVar } from '@/env';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { nanoid } from '@reduxjs/toolkit';
|
||||
import { useDropzone } from 'react-dropzone';
|
||||
@@ -575,7 +576,7 @@ function Upload({
|
||||
JSON.stringify(optionsToConfig(retrievalOptions)),
|
||||
);
|
||||
|
||||
const apiHost = import.meta.env.VITE_API_HOST;
|
||||
const apiHost = envVar('VITE_API_HOST');
|
||||
const xhr = new XMLHttpRequest();
|
||||
|
||||
dispatch(
|
||||
@@ -706,7 +707,7 @@ function Upload({
|
||||
|
||||
formData.append('data', JSON.stringify(configData));
|
||||
|
||||
const apiHost: string = import.meta.env.VITE_API_HOST;
|
||||
const apiHost: string = envVar('VITE_API_HOST');
|
||||
const endpoint =
|
||||
ingestor.type === 'local_file'
|
||||
? `${apiHost}/api/upload`
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { envVar } from '@/env';
|
||||
import CrawlerIcon from '../../assets/crawler.svg';
|
||||
import FileUploadIcon from '../../assets/file_upload.svg';
|
||||
import UrlIcon from '../../assets/url.svg';
|
||||
@@ -146,7 +147,7 @@ export const IngestorFormSchemas: IngestorSchema[] = [
|
||||
icon: DriveIcon,
|
||||
heading: 'Upload from Google Drive',
|
||||
validate: () => {
|
||||
const googleClientId = import.meta.env.VITE_GOOGLE_CLIENT_ID;
|
||||
const googleClientId = envVar('VITE_GOOGLE_CLIENT_ID');
|
||||
return !!googleClientId;
|
||||
},
|
||||
fields: [
|
||||
@@ -208,7 +209,7 @@ export const IngestorFormSchemas: IngestorSchema[] = [
|
||||
icon: SharePoint,
|
||||
heading: 'Upload from Share Point',
|
||||
validate: () => {
|
||||
const sharePointClientId = import.meta.env.VITE_SHARE_POINT_CLIENT_ID;
|
||||
const sharePointClientId = envVar('VITE_SHARE_POINT_CLIENT_ID');
|
||||
return !!sharePointClientId;
|
||||
},
|
||||
fields: [
|
||||
@@ -226,7 +227,7 @@ export const IngestorFormSchemas: IngestorSchema[] = [
|
||||
icon: ConfluenceIcon,
|
||||
heading: 'Upload from Confluence',
|
||||
validate: () => {
|
||||
const confluenceClientId = import.meta.env.VITE_CONFLUENCE_CLIENT_ID;
|
||||
const confluenceClientId = envVar('VITE_CONFLUENCE_CLIENT_ID');
|
||||
return !!confluenceClientId;
|
||||
},
|
||||
fields: [
|
||||
|
||||
@@ -513,29 +513,32 @@ function Configure-DocProcessing {
|
||||
Write-ColorText "PDF-as-image parsing enabled." -ForegroundColor "Green"
|
||||
}
|
||||
|
||||
# OCR needs the tesseract binary, an optional system package that only
|
||||
# locally built images can include (INSTALL_TESSERACT build arg). The
|
||||
# pre-built Docker Hub images ship without it, so there OCR stays off
|
||||
# (its default) rather than being switched on to fail on every scan.
|
||||
if ($COMPOSE_FILE -ne $COMPOSE_FILE_LOCAL) {
|
||||
Write-ColorText "OCR for scanned PDFs and images stays off: the pre-built Docker Hub images do not include tesseract. To use OCR, rerun setup and choose option 5 (build images locally), or use a DeepSeek-OCR endpoint by adding OCR_ENABLED=true, OCR_ENGINE=deepseek and OCR_DEEPSEEK_URL=<endpoint> to .env." -ForegroundColor "Yellow"
|
||||
# OCR needs the tesseract binary. The default (slim) images ship without
|
||||
# it; the pre-built "-docling" image variant bakes tesseract, the docling
|
||||
# layout engine and its models in, so with Docker Hub images OCR means
|
||||
# switching the variant. Locally built images get it via build args.
|
||||
$ocr_enabled = Read-Host "Enable OCR for scanned PDFs and images? (y/N)"
|
||||
if (-not ($ocr_enabled -eq "y" -or $ocr_enabled -eq "Y")) {
|
||||
return
|
||||
}
|
||||
|
||||
$ocr_enabled = Read-Host "Enable OCR for scanned PDFs and images? (y/N)"
|
||||
if ($ocr_enabled -eq "y" -or $ocr_enabled -eq "Y") {
|
||||
"OCR_ENABLED=true" | Add-Content -Path $ENV_FILE -Encoding utf8
|
||||
# Bakes tesseract into the locally built images (docker compose
|
||||
# --env-file .env build).
|
||||
"INSTALL_TESSERACT=true" | Add-Content -Path $ENV_FILE -Encoding utf8
|
||||
Write-ColorText "OCR enabled. tesseract will be built into the images (INSTALL_TESSERACT=true); for a DeepSeek-OCR endpoint instead, set OCR_ENGINE=deepseek and OCR_DEEPSEEK_URL=<endpoint> in .env." -ForegroundColor "Green"
|
||||
$docling_ocr = Read-Host "Also install the Docling layout engine for OCR (better tables/reading order, several GB heavier)? (y/N)"
|
||||
if ($docling_ocr -eq "y" -or $docling_ocr -eq "Y") {
|
||||
# Locally built images include docling via this build arg; it becomes
|
||||
# the OCR backend automatically (OCR_BACKEND=auto).
|
||||
"INSTALL_DOCLING=true" | Add-Content -Path $ENV_FILE -Encoding utf8
|
||||
Write-ColorText "Docling will be built into locally built images (docker compose --env-file .env build). Pre-built Docker Hub images do not include it." -ForegroundColor "Green"
|
||||
}
|
||||
"OCR_ENABLED=true" | Add-Content -Path $ENV_FILE -Encoding utf8
|
||||
if ($COMPOSE_FILE -ne $COMPOSE_FILE_LOCAL) {
|
||||
# Pre-built images: pull arc53/docsgpt:<tag>-docling instead of the
|
||||
# slim default (about 1.5 GB more to download).
|
||||
"DOCSGPT_IMAGE_VARIANT=-docling" | Add-Content -Path $ENV_FILE -Encoding utf8
|
||||
Write-ColorText "OCR enabled. The -docling image variant will be pulled (tesseract, docling layout engine and its models included). For a DeepSeek-OCR endpoint instead, set OCR_ENGINE=deepseek and OCR_DEEPSEEK_URL=<endpoint> in .env." -ForegroundColor "Green"
|
||||
return
|
||||
}
|
||||
# Bakes tesseract into the locally built images (docker compose
|
||||
# --env-file .env build).
|
||||
"INSTALL_TESSERACT=true" | Add-Content -Path $ENV_FILE -Encoding utf8
|
||||
Write-ColorText "OCR enabled. tesseract will be built into the images (INSTALL_TESSERACT=true); for a DeepSeek-OCR endpoint instead, set OCR_ENGINE=deepseek and OCR_DEEPSEEK_URL=<endpoint> in .env." -ForegroundColor "Green"
|
||||
$docling_ocr = Read-Host "Also install the Docling layout engine for OCR (better tables/reading order, several GB heavier)? (y/N)"
|
||||
if ($docling_ocr -eq "y" -or $docling_ocr -eq "Y") {
|
||||
# Locally built images include docling via this build arg; it becomes
|
||||
# the OCR backend automatically (OCR_BACKEND=auto).
|
||||
"INSTALL_DOCLING=true" | Add-Content -Path $ENV_FILE -Encoding utf8
|
||||
Write-ColorText "Docling will be built into locally built images (docker compose --env-file .env build)." -ForegroundColor "Green"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -367,29 +367,32 @@ configure_doc_processing() {
|
||||
echo -e "${GREEN}PDF-as-image parsing enabled.${NC}"
|
||||
fi
|
||||
|
||||
# OCR needs the tesseract binary, an optional system package that only
|
||||
# locally built images can include (INSTALL_TESSERACT build arg). The
|
||||
# pre-built Docker Hub images ship without it, so there OCR stays off
|
||||
# (its default) rather than being switched on to fail on every scan.
|
||||
if [[ "$COMPOSE_FILE" != "$COMPOSE_FILE_LOCAL" ]]; then
|
||||
echo -e "${YELLOW}OCR for scanned PDFs and images stays off: the pre-built Docker Hub images do not include tesseract. To use OCR, rerun setup and choose option 5 (build images locally), or use a DeepSeek-OCR endpoint by adding OCR_ENABLED=true, OCR_ENGINE=deepseek and OCR_DEEPSEEK_URL=<endpoint> to .env.${NC}"
|
||||
# OCR needs the tesseract binary. The default (slim) images ship without
|
||||
# it; the pre-built "-docling" image variant bakes tesseract, the docling
|
||||
# layout engine and its models in, so with Docker Hub images OCR means
|
||||
# switching the variant. Locally built images get it via build args.
|
||||
read -p "$(echo -e "${DEFAULT_FG}Enable OCR for scanned PDFs and images? (y/N): ${NC}")" ocr_enabled
|
||||
if [[ ! "$ocr_enabled" =~ ^[yY]$ ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
read -p "$(echo -e "${DEFAULT_FG}Enable OCR for scanned PDFs and images? (y/N): ${NC}")" ocr_enabled
|
||||
if [[ "$ocr_enabled" =~ ^[yY]$ ]]; then
|
||||
echo "OCR_ENABLED=true" >> "$ENV_FILE"
|
||||
# Bakes tesseract into the locally built images (docker compose
|
||||
# --env-file .env build).
|
||||
echo "INSTALL_TESSERACT=true" >> "$ENV_FILE"
|
||||
echo -e "${GREEN}OCR enabled. tesseract will be built into the images (INSTALL_TESSERACT=true); for a DeepSeek-OCR endpoint instead, set OCR_ENGINE=deepseek and OCR_DEEPSEEK_URL=<endpoint> in .env.${NC}"
|
||||
read -p "$(echo -e "${DEFAULT_FG}Also install the Docling layout engine for OCR (better tables/reading order, several GB heavier)? (y/N): ${NC}")" docling_ocr
|
||||
if [[ "$docling_ocr" =~ ^[yY]$ ]]; then
|
||||
# Locally built images include docling via this build arg; it becomes
|
||||
# the OCR backend automatically (OCR_BACKEND=auto).
|
||||
echo "INSTALL_DOCLING=true" >> "$ENV_FILE"
|
||||
echo -e "${GREEN}Docling will be built into locally built images (docker compose --env-file .env build). Pre-built Docker Hub images do not include it.${NC}"
|
||||
fi
|
||||
echo "OCR_ENABLED=true" >> "$ENV_FILE"
|
||||
if [[ "$COMPOSE_FILE" != "$COMPOSE_FILE_LOCAL" ]]; then
|
||||
# Pre-built images: pull arc53/docsgpt:<tag>-docling instead of the
|
||||
# slim default (about 1.5 GB more to download).
|
||||
echo "DOCSGPT_IMAGE_VARIANT=-docling" >> "$ENV_FILE"
|
||||
echo -e "${GREEN}OCR enabled. The -docling image variant will be pulled (tesseract, docling layout engine and its models included). For a DeepSeek-OCR endpoint instead, set OCR_ENGINE=deepseek and OCR_DEEPSEEK_URL=<endpoint> in .env.${NC}"
|
||||
return
|
||||
fi
|
||||
# Bakes tesseract into the locally built images (docker compose
|
||||
# --env-file .env build).
|
||||
echo "INSTALL_TESSERACT=true" >> "$ENV_FILE"
|
||||
echo -e "${GREEN}OCR enabled. tesseract will be built into the images (INSTALL_TESSERACT=true); for a DeepSeek-OCR endpoint instead, set OCR_ENGINE=deepseek and OCR_DEEPSEEK_URL=<endpoint> in .env.${NC}"
|
||||
read -p "$(echo -e "${DEFAULT_FG}Also install the Docling layout engine for OCR (better tables/reading order, several GB heavier)? (y/N): ${NC}")" docling_ocr
|
||||
if [[ "$docling_ocr" =~ ^[yY]$ ]]; then
|
||||
# Locally built images include docling via this build arg; it becomes
|
||||
# the OCR backend automatically (OCR_BACKEND=auto).
|
||||
echo "INSTALL_DOCLING=true" >> "$ENV_FILE"
|
||||
echo -e "${GREEN}Docling will be built into locally built images (docker compose --env-file .env build).${NC}"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user