mirror of
https://github.com/docling-project/docling-serve.git
synced 2025-11-29 08:33:50 +00:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d5557fad9f | ||
|
|
36967f7f61 | ||
|
|
3b54d9b6ef | ||
|
|
4877248368 | ||
|
|
ec33a61faa | ||
|
|
663e03303a | ||
|
|
c64a450bf9 | ||
|
|
ae3b4906f1 | ||
|
|
7a351fcdea | ||
|
|
1615f977a2 | ||
|
|
1bf487b18e |
12
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
12
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
<!-- Thank you for contributing to Docling! -->
|
||||
|
||||
<!-- STEPS TO FOLLOW:
|
||||
1. Add a description of the changes (frequently the same as the commit description)
|
||||
2. Enter the issue number next to "Resolves #" below (if there is no tracking issue resolved, **remove that section**)
|
||||
3. Make sure the PR title follows the **Commit Message Formatting**: https://www.conventionalcommits.org/en/v1.0.0/#summary.
|
||||
-->
|
||||
|
||||
<!-- Uncomment this section with the issue number if an issue is being resolved
|
||||
**Issue resolved by this Pull Request:**
|
||||
Resolves #
|
||||
--->
|
||||
23
.github/SECURITY.md
vendored
Normal file
23
.github/SECURITY.md
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
# Security and Disclosure Information Policy for the Docling Project
|
||||
|
||||
The Docling team and community take security bugs seriously. We appreciate your efforts to responsibly disclose your findings, and will make every effort to acknowledge your contributions.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
If you think you've identified a security issue in an Docling project repository, please DO NOT report the issue publicly via the GitHub issue tracker, etc.
|
||||
|
||||
Instead, send an email with as many details as possible to [deepsearch-core@zurich.ibm.com](mailto:deepsearch-core@zurich.ibm.com). This is a private mailing list for the maintainers team.
|
||||
|
||||
Please do not create a public issue.
|
||||
|
||||
## Security Vulnerability Response
|
||||
|
||||
Each report is acknowledged and analyzed by the core maintainers within 3 working days.
|
||||
|
||||
Any vulnerability information shared with core maintainers stays within the Docling project and will not be disseminated to other projects unless it is necessary to get the issue fixed.
|
||||
|
||||
After the initial reply to your report, the security team will keep you informed of the progress towards a fix and full announcement, and may ask for additional information or guidance.
|
||||
|
||||
## Security Alerts
|
||||
|
||||
We will send announcements of security vulnerabilities and steps to remediate on the [Docling announcements](https://github.com/DS4SD/docling/discussions/categories/announcements).
|
||||
19
.github/actions/setup-poetry/action.yml
vendored
19
.github/actions/setup-poetry/action.yml
vendored
@@ -1,19 +0,0 @@
|
||||
name: 'Set up Poetry and install'
|
||||
description: 'Set up a specific version of Poetry and install dependencies using caching.'
|
||||
inputs:
|
||||
python-version:
|
||||
description: "Version range or exact version of Python or PyPy to use, using SemVer's version range syntax."
|
||||
default: '3.11'
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: Install poetry
|
||||
run: pipx install poetry==1.8.3
|
||||
shell: bash
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ inputs.python-version }}
|
||||
cache: 'poetry'
|
||||
- name: Install dependencies
|
||||
run: poetry install --all-extras
|
||||
shell: bash
|
||||
9
.github/mergify.yml
vendored
Normal file
9
.github/mergify.yml
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
merge_protections:
|
||||
- name: Enforce conventional commit
|
||||
description: Make sure that we follow https://www.conventionalcommits.org/en/v1.0.0/
|
||||
if:
|
||||
- base = main
|
||||
success_conditions:
|
||||
- "title ~=
|
||||
^(fix|feat|docs|style|refactor|perf|test|build|ci|chore|revert)(?:\\(.+\
|
||||
\\))?(!)?:"
|
||||
39
.github/scripts/release.sh
vendored
Executable file
39
.github/scripts/release.sh
vendored
Executable file
@@ -0,0 +1,39 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e # trigger failure on error - do not remove!
|
||||
set -x # display command on output
|
||||
|
||||
if [ -z "${TARGET_VERSION}" ]; then
|
||||
>&2 echo "No TARGET_VERSION specified"
|
||||
exit 1
|
||||
fi
|
||||
CHGLOG_FILE="${CHGLOG_FILE:-CHANGELOG.md}"
|
||||
|
||||
# update package version
|
||||
uvx --from=toml-cli toml set --toml-path=pyproject.toml project.version "${TARGET_VERSION}"
|
||||
|
||||
# collect release notes
|
||||
REL_NOTES=$(mktemp)
|
||||
uv run --no-sync semantic-release changelog --unreleased >> "${REL_NOTES}"
|
||||
|
||||
# update changelog
|
||||
TMP_CHGLOG=$(mktemp)
|
||||
TARGET_TAG_NAME="v${TARGET_VERSION}"
|
||||
RELEASE_URL="$(gh repo view --json url -q ".url")/releases/tag/${TARGET_TAG_NAME}"
|
||||
printf "## [${TARGET_TAG_NAME}](${RELEASE_URL}) - $(date -Idate)\n\n" >> "${TMP_CHGLOG}"
|
||||
cat "${REL_NOTES}" >> "${TMP_CHGLOG}"
|
||||
if [ -f "${CHGLOG_FILE}" ]; then
|
||||
printf "\n" | cat - "${CHGLOG_FILE}" >> "${TMP_CHGLOG}"
|
||||
fi
|
||||
mv "${TMP_CHGLOG}" "${CHGLOG_FILE}"
|
||||
|
||||
# push changes
|
||||
git config --global user.name 'github-actions[bot]'
|
||||
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
|
||||
git add pyproject.toml "${CHGLOG_FILE}"
|
||||
COMMIT_MSG="chore: bump version to ${TARGET_VERSION} [skip ci]"
|
||||
git commit -m "${COMMIT_MSG}"
|
||||
git push origin main
|
||||
|
||||
# create GitHub release (incl. Git tag)
|
||||
gh release create "${TARGET_TAG_NAME}" -F "${REL_NOTES}"
|
||||
59
.github/workflows/cd.yml
vendored
Normal file
59
.github/workflows/cd.yml
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
name: "Run CD"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
code-checks:
|
||||
uses: ./.github/workflows/job-checks.yml
|
||||
pre-release-check:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
TARGET_TAG_V: ${{ steps.version_check.outputs.TRGT_VERSION }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # for fetching tags, required for semantic-release
|
||||
- name: Install uv and set the python version
|
||||
uses: astral-sh/setup-uv@v5
|
||||
with:
|
||||
enable-cache: true
|
||||
- name: Install dependencies
|
||||
run: uv sync --only-dev
|
||||
- name: Check version of potential release
|
||||
id: version_check
|
||||
run: |
|
||||
TRGT_VERSION=$(uv run --no-sync semantic-release print-version)
|
||||
echo "TRGT_VERSION=${TRGT_VERSION}" >> "$GITHUB_OUTPUT"
|
||||
echo "${TRGT_VERSION}"
|
||||
- name: Check notes of potential release
|
||||
run: uv run --no-sync semantic-release changelog --unreleased
|
||||
release:
|
||||
needs: [code-checks, pre-release-check]
|
||||
if: needs.pre-release-check.outputs.TARGET_TAG_V != ''
|
||||
environment: auto-release
|
||||
runs-on: ubuntu-latest
|
||||
concurrency: release
|
||||
steps:
|
||||
- uses: actions/create-github-app-token@v1
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ vars.CI_APP_ID }}
|
||||
private-key: ${{ secrets.CI_PRIVATE_KEY }}
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ steps.app-token.outputs.token }}
|
||||
fetch-depth: 0 # for fetching tags, required for semantic-release
|
||||
- name: Install uv and set the python version
|
||||
uses: astral-sh/setup-uv@v5
|
||||
with:
|
||||
enable-cache: true
|
||||
- name: Install dependencies
|
||||
run: uv sync --only-dev
|
||||
- name: Run release script
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
TARGET_VERSION: ${{ needs.pre-release-check.outputs.TARGET_TAG_V }}
|
||||
CHGLOG_FILE: CHANGELOG.md
|
||||
run: ./.github/scripts/release.sh
|
||||
shell: bash
|
||||
43
.github/workflows/ci-images-dryrun.yml
vendored
Normal file
43
.github/workflows/ci-images-dryrun.yml
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
name: Dry run docling-serve image building
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build_cpu_image:
|
||||
name: Build docling-serve "CPU only" container image
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
attestations: write
|
||||
id-token: write
|
||||
|
||||
uses: ./.github/workflows/job-image.yml
|
||||
with:
|
||||
publish: false
|
||||
build_args: |
|
||||
UV_SYNC_EXTRA_ARGS=--no-extra cu124
|
||||
ghcr_image_name: ds4sd/docling-serve-cpu
|
||||
quay_image_name: ""
|
||||
|
||||
|
||||
build_gpu_image:
|
||||
name: Build docling-serve (with GPU support) container image
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
attestations: write
|
||||
id-token: write
|
||||
|
||||
uses: ./.github/workflows/job-image.yml
|
||||
with:
|
||||
publish: false
|
||||
build_args: |
|
||||
UV_SYNC_EXTRA_ARGS=--no-extra cpu
|
||||
platforms: linux/amd64
|
||||
ghcr_image_name: ds4sd/docling-serve
|
||||
quay_image_name: ""
|
||||
25
.github/workflows/ci.yml
vendored
Normal file
25
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
name: "Run CI"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["main"]
|
||||
pull_request:
|
||||
branches: ["main"]
|
||||
|
||||
jobs:
|
||||
code-checks:
|
||||
# if: ${{ github.event_name == 'push' || (github.event.pull_request.head.repo.full_name != 'DS4SD/docling-serve' && github.event.pull_request.head.repo.full_name != 'ds4sd/docling-serve') }}
|
||||
uses: ./.github/workflows/job-checks.yml
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
attestations: write
|
||||
id-token: write
|
||||
|
||||
build-images:
|
||||
uses: ./.github/workflows/ci-images-dryrun.yml
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
attestations: write
|
||||
id-token: write
|
||||
105
.github/workflows/images-dryrun.yml
vendored
105
.github/workflows/images-dryrun.yml
vendored
@@ -1,105 +0,0 @@
|
||||
name: Dry run docling-serve image building
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: ["main"]
|
||||
|
||||
env:
|
||||
GHCR_REGISTRY: ghcr.io
|
||||
GHCR_DOCLING_SERVE_CPU_IMAGE_NAME: ds4sd/docling-serve-cpu
|
||||
GHCR_DOCLING_SERVE_GPU_IMAGE_NAME: ds4sd/docling-serve
|
||||
|
||||
jobs:
|
||||
build_cpu_image:
|
||||
name: Build docling-serve "CPU only" container image
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
attestations: write
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Cache Docker layers
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: /tmp/.buildx-cache
|
||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-buildx-
|
||||
|
||||
- name: Extract metadata (tags, labels) for docling-serve (CPU only) ghcr image
|
||||
id: ghcr_serve_cpu_meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.GHCR_REGISTRY }}/${{ env.GHCR_DOCLING_SERVE_CPU_IMAGE_NAME }}
|
||||
|
||||
- name: Build docling-serve-cpu image
|
||||
id: build-serve-cpu-ghcr
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: false
|
||||
tags: ${{ steps.ghcr_serve_cpu_meta.outputs.tags }}
|
||||
labels: ${{ steps.ghcr_serve_cpu_meta.outputs.labels }}
|
||||
platforms: linux/amd64, linux/arm64
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
file: Containerfile
|
||||
build-args: |
|
||||
--build-arg CPU_ONLY=true
|
||||
|
||||
- name: Remove Local Docker Images
|
||||
run: |
|
||||
docker image prune -af
|
||||
|
||||
build_gpu_image:
|
||||
name: Build docling-serve (with GPU support) container image
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
attestations: write
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Cache Docker layers
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: /tmp/.buildx-cache
|
||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-buildx-
|
||||
|
||||
- name: Extract metadata (tags, labels) for docling-serve (GPU) ghcr image
|
||||
id: ghcr_serve_gpu_meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.GHCR_REGISTRY }}/${{ env.GHCR_DOCLING_SERVE_GPU_IMAGE_NAME }}
|
||||
|
||||
- name: Build docling-serve (GPU) image
|
||||
id: build-serve-gpu-ghcr
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: false
|
||||
tags: ${{ steps.ghcr_serve_gpu_meta.outputs.tags }}
|
||||
labels: ${{ steps.ghcr_serve_gpu_meta.outputs.labels }}
|
||||
platforms: linux/amd64,linux/arm64
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
file: Containerfile
|
||||
build-args: |
|
||||
--build-arg CPU_ONLY=false
|
||||
196
.github/workflows/images.yml
vendored
196
.github/workflows/images.yml
vendored
@@ -7,190 +7,54 @@ on:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
env:
|
||||
GHCR_REGISTRY: ghcr.io
|
||||
GHCR_DOCLING_SERVE_CPU_IMAGE_NAME: ds4sd/docling-serve-cpu
|
||||
GHCR_DOCLING_SERVE_GPU_IMAGE_NAME: ds4sd/docling-serve
|
||||
QUAY_REGISTRY: quay.io
|
||||
QUAY_DOCLING_SERVE_CPU_IMAGE_NAME: ds4sd/docling-serve-cpu
|
||||
QUAY_DOCLING_SERVE_GPU_IMAGE_NAME: ds4sd/docling-serve
|
||||
# env:
|
||||
# GHCR_REGISTRY: ghcr.io
|
||||
# # GHCR_DOCLING_SERVE_CPU_IMAGE_NAME: ds4sd/docling-serve-cpu
|
||||
# # GHCR_DOCLING_SERVE_GPU_IMAGE_NAME: ds4sd/docling-serve
|
||||
# QUAY_REGISTRY: quay.io
|
||||
# # QUAY_DOCLING_SERVE_CPU_IMAGE_NAME: ds4sd/docling-serve-cpu
|
||||
# # QUAY_DOCLING_SERVE_GPU_IMAGE_NAME: ds4sd/docling-serve
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build_and_publish_cpu_images:
|
||||
name: Push docling-serve "CPU only" container image to GHCR and QUAY
|
||||
runs-on: ubuntu-latest
|
||||
environment: registry-creds
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
attestations: write
|
||||
id-token: write
|
||||
secrets: inherit
|
||||
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v4
|
||||
uses: ./.github/workflows/job-image.yml
|
||||
with:
|
||||
publish: true
|
||||
environment: registry-creds
|
||||
build_args: |
|
||||
UV_SYNC_EXTRA_ARGS=--no-extra cu124
|
||||
ghcr_image_name: ds4sd/docling-serve-cpu
|
||||
quay_image_name: ds4sd/docling-serve-cpu
|
||||
|
||||
- name: Log in to the GHCR container image registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.GHCR_REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Log in to the Quay container image registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.QUAY_REGISTRY }}
|
||||
username: ${{ secrets.QUAY_USERNAME }}
|
||||
password: ${{ secrets.QUAY_TOKEN }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Cache Docker layers
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: /tmp/.buildx-cache
|
||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-buildx-
|
||||
|
||||
- name: Extract metadata (tags, labels) for docling-serve (CPU only) ghcr image
|
||||
id: ghcr_serve_cpu_meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.GHCR_REGISTRY }}/${{ env.GHCR_DOCLING_SERVE_CPU_IMAGE_NAME }}
|
||||
|
||||
- name: Build and push docling-serve-cpu image to ghcr.io
|
||||
id: push-serve-cpu-ghcr
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: ${{ steps.ghcr_serve_cpu_meta.outputs.tags }}
|
||||
labels: ${{ steps.ghcr_serve_cpu_meta.outputs.labels }}
|
||||
platforms: linux/amd64, linux/arm64
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
file: Containerfile
|
||||
build-args: |
|
||||
--build-arg CPU_ONLY=true
|
||||
|
||||
- name: Generate artifact attestation
|
||||
uses: actions/attest-build-provenance@v1
|
||||
with:
|
||||
subject-name: ${{ env.GHCR_REGISTRY }}/${{ env.GHCR_DOCLING_SERVE_CPU_IMAGE_NAME}}
|
||||
subject-digest: ${{ steps.push-serve-cpu-ghcr.outputs.digest }}
|
||||
push-to-registry: true
|
||||
|
||||
- name: Extract metadata (tags, labels) for docling-serve (CPU only) quay image
|
||||
id: quay_serve_cpu_meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.QUAY_REGISTRY }}/${{ env.QUAY_DOCLING_SERVE_CPU_IMAGE_NAME }}
|
||||
|
||||
- name: Build and push docling-serve-cpu image to quay.io
|
||||
id: push-serve-cpu-quay
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: ${{ steps.quay_serve_cpu_meta.outputs.tags }}
|
||||
labels: ${{ steps.quay_serve_cpu_meta.outputs.labels }}
|
||||
platforms: linux/amd64, linux/arm64
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
file: Containerfile
|
||||
build-args: |
|
||||
--build-arg CPU_ONLY=true
|
||||
- name: Remove Local Docker Images
|
||||
run: |
|
||||
docker image prune -af
|
||||
|
||||
build_and_publish_gpu_images:
|
||||
name: Push docling-serve (with GPU support) container image to GHCR and QUAY
|
||||
runs-on: ubuntu-latest
|
||||
environment: registry-creds
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
attestations: write
|
||||
id-token: write
|
||||
secrets: inherit
|
||||
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v4
|
||||
uses: ./.github/workflows/job-image.yml
|
||||
with:
|
||||
publish: true
|
||||
environment: registry-creds
|
||||
build_args: |
|
||||
UV_SYNC_EXTRA_ARGS=--no-extra cpu
|
||||
platforms: linux/amd64
|
||||
ghcr_image_name: ds4sd/docling-serve
|
||||
quay_image_name: ds4sd/docling-serve
|
||||
|
||||
- name: Log in to the GHCR container image registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.GHCR_REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Log in to the Quay container image registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.QUAY_REGISTRY }}
|
||||
username: ${{ secrets.QUAY_USERNAME }}
|
||||
password: ${{ secrets.QUAY_TOKEN }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Cache Docker layers
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: /tmp/.buildx-cache
|
||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-buildx-
|
||||
|
||||
- name: Extract metadata (tags, labels) for docling-serve (GPU) ghcr image
|
||||
id: ghcr_serve_gpu_meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.GHCR_REGISTRY }}/${{ env.GHCR_DOCLING_SERVE_GPU_IMAGE_NAME }}
|
||||
|
||||
- name: Build and push docling-serve (GPU) image to ghcr.io
|
||||
id: push-serve-gpu-ghcr
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: ${{ steps.ghcr_serve_gpu_meta.outputs.tags }}
|
||||
labels: ${{ steps.ghcr_serve_gpu_meta.outputs.labels }}
|
||||
platforms: linux/amd64,linux/arm64
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
file: Containerfile
|
||||
build-args: |
|
||||
--build-arg CPU_ONLY=false
|
||||
|
||||
- name: Generate artifact attestation
|
||||
uses: actions/attest-build-provenance@v1
|
||||
with:
|
||||
subject-name: ${{ env.GHCR_REGISTRY }}/${{ env.GHCR_DOCLING_SERVE_GPU_IMAGE_NAME}}
|
||||
subject-digest: ${{ steps.push-serve-gpu-ghcr.outputs.digest }}
|
||||
push-to-registry: true
|
||||
|
||||
- name: Extract metadata (tags, labels) for docling-serve (GPU) quay image
|
||||
id: quay_serve_gpu_meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.QUAY_REGISTRY }}/${{ env.QUAY_DOCLING_SERVE_GPU_IMAGE_NAME }}
|
||||
|
||||
- name: Build and push docling-serve (GPU) image to quay.io
|
||||
id: push-serve-gpu-quay
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: ${{ steps.quay_serve_gpu_meta.outputs.tags }}
|
||||
labels: ${{ steps.quay_serve_gpu_meta.outputs.labels }}
|
||||
platforms: linux/amd64,linux/arm64
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
file: Containerfile
|
||||
build-args: |
|
||||
--build-arg CPU_ONLY=false
|
||||
|
||||
@@ -1,27 +1,25 @@
|
||||
name: Run linter checks
|
||||
on:
|
||||
push:
|
||||
branches: ["main"]
|
||||
pull_request:
|
||||
branches: ["main"]
|
||||
name: Run checks
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
jobs:
|
||||
py-lint:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ['3.11']
|
||||
python-version: ['3.12']
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/setup-poetry
|
||||
- name: Install uv and set the python version
|
||||
uses: astral-sh/setup-uv@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
enable-cache: true
|
||||
- name: Install dependencies
|
||||
run: uv sync --all-extras --no-extra cu124
|
||||
- name: Run styling check
|
||||
run: poetry run pre-commit run --all-files
|
||||
run: uv run --no-sync pre-commit run --all-files
|
||||
|
||||
markdown-lint:
|
||||
runs-on: ubuntu-latest
|
||||
141
.github/workflows/job-image.yml
vendored
Normal file
141
.github/workflows/job-image.yml
vendored
Normal file
@@ -0,0 +1,141 @@
|
||||
name: Build docling-serve container image
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
build_args:
|
||||
type: string
|
||||
description: "Extra build arguments for the build."
|
||||
default: ""
|
||||
ghcr_image_name:
|
||||
type: string
|
||||
description: "Name of the image for GHCR."
|
||||
quay_image_name:
|
||||
type: string
|
||||
description: "Name of the image Quay."
|
||||
platforms:
|
||||
type: string
|
||||
description: "Platform argument for building images."
|
||||
default: linux/amd64, linux/arm64
|
||||
publish:
|
||||
type: boolean
|
||||
description: "If true, the images will be published."
|
||||
default: false
|
||||
environment:
|
||||
type: string
|
||||
description: "GH Action environment"
|
||||
default: ""
|
||||
|
||||
env:
|
||||
GHCR_REGISTRY: ghcr.io
|
||||
# GHCR_DOCLING_SERVE_CPU_IMAGE_NAME: ds4sd/docling-serve-cpu
|
||||
# GHCR_DOCLING_SERVE_GPU_IMAGE_NAME: ds4sd/docling-serve
|
||||
QUAY_REGISTRY: quay.io
|
||||
# QUAY_DOCLING_SERVE_CPU_IMAGE_NAME: ds4sd/docling-serve-cpu
|
||||
# QUAY_DOCLING_SERVE_GPU_IMAGE_NAME: ds4sd/docling-serve
|
||||
|
||||
jobs:
|
||||
image:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
attestations: write
|
||||
id-token: write
|
||||
environment: ${{ inputs.environment }}
|
||||
|
||||
steps:
|
||||
- name: Free up space in github runner
|
||||
# Free space as indicated here : https://github.com/actions/runner-images/issues/2840#issuecomment-790492173
|
||||
run: |
|
||||
df -h
|
||||
sudo rm -rf "/usr/local/share/boost"
|
||||
sudo rm -rf "$AGENT_TOOLSDIRECTORY"
|
||||
sudo rm -rf /usr/share/dotnet /opt/ghc /usr/local/lib/android /usr/local/share/powershell /usr/share/swift /usr/local/.ghcup
|
||||
# shellcheck disable=SC2046
|
||||
sudo docker rmi "$(docker image ls -aq)" >/dev/null 2>&1 || true
|
||||
df -h
|
||||
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Log in to the GHCR container image registry
|
||||
if: ${{ inputs.publish }}
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.GHCR_REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Log in to the Quay container image registry
|
||||
if: ${{ inputs.publish }}
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.QUAY_REGISTRY }}
|
||||
username: ${{ secrets.QUAY_USERNAME }}
|
||||
password: ${{ secrets.QUAY_TOKEN }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Cache Docker layers
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: /tmp/.buildx-cache
|
||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-buildx-
|
||||
|
||||
- name: Extract metadata (tags, labels) for docling-serve ghcr image
|
||||
id: ghcr_meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.GHCR_REGISTRY }}/${{ inputs.ghcr_image_name }}
|
||||
|
||||
- name: Build and push image to ghcr.io
|
||||
id: ghcr_push
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: ${{ inputs.publish }}
|
||||
tags: ${{ steps.ghcr_meta.outputs.tags }}
|
||||
labels: ${{ steps.ghcr_meta.outputs.labels }}
|
||||
platforms: ${{ inputs.platforms}}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
file: Containerfile
|
||||
build-args: ${{ inputs.build_args }}
|
||||
|
||||
- name: Generate artifact attestation
|
||||
if: ${{ inputs.publish }}
|
||||
uses: actions/attest-build-provenance@v1
|
||||
with:
|
||||
subject-name: ${{ env.GHCR_REGISTRY }}/${{ inputs.ghcr_image_name }}
|
||||
subject-digest: ${{ steps.ghcr_push.outputs.digest }}
|
||||
push-to-registry: true
|
||||
|
||||
- name: Extract metadata (tags, labels) for docling-serve quay image
|
||||
if: ${{ inputs.publish }}
|
||||
id: quay_meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.QUAY_REGISTRY }}/${{ inputs.quay_image_name }}
|
||||
|
||||
- name: Build and push image to quay.io
|
||||
if: ${{ inputs.publish }}
|
||||
# id: push-serve-cpu-quay
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: ${{ inputs.publish }}
|
||||
tags: ${{ steps.quay_meta.outputs.tags }}
|
||||
labels: ${{ steps.quay_meta.outputs.labels }}
|
||||
platforms: ${{ inputs.platforms}}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
file: Containerfile
|
||||
build-args: ${{ inputs.build_args }}
|
||||
|
||||
- name: Remove Local Docker Images
|
||||
run: |
|
||||
docker image prune -af
|
||||
32
.github/workflows/pypi.yml
vendored
Normal file
32
.github/workflows/pypi.yml
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
name: "Build and publish package"
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build-and-publish:
|
||||
runs-on: ubuntu-latest
|
||||
environment:
|
||||
name: pypi
|
||||
url: https://pypi.org/p/docling-serve # Replace <package-name> with your PyPI project name
|
||||
permissions:
|
||||
id-token: write # IMPORTANT: mandatory for trusted publishing
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install uv and set the python version
|
||||
uses: astral-sh/setup-uv@v5
|
||||
with:
|
||||
enable-cache: true
|
||||
- name: Install dependencies
|
||||
run: uv sync --all-extras --no-extra cu124
|
||||
- name: Build
|
||||
run: uv build
|
||||
- name: Publish distribution 📦 to PyPI
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
with:
|
||||
# currently not working with reusable workflows
|
||||
attestations: false
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,5 +1,7 @@
|
||||
model_artifacts/
|
||||
scratch/
|
||||
.md-lint
|
||||
actionlint
|
||||
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/python,macos,virtualenv,pycharm,visualstudiocode,emacs,vim,jupyternotebooks
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=python,macos,virtualenv,pycharm,visualstudiocode,emacs,vim,jupyternotebooks
|
||||
|
||||
@@ -1,49 +1,24 @@
|
||||
fail_fast: true
|
||||
repos:
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: system
|
||||
name: Black
|
||||
entry: poetry run black docling_serve tests
|
||||
pass_filenames: false
|
||||
language: system
|
||||
files: '\.py$'
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: system
|
||||
name: isort
|
||||
entry: poetry run isort docling_serve tests
|
||||
pass_filenames: false
|
||||
language: system
|
||||
files: '\.py$'
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: autoflake
|
||||
name: autoflake
|
||||
entry: poetry run autoflake docling_serve tests
|
||||
pass_filenames: false
|
||||
language: system
|
||||
files: '\.py$'
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: system
|
||||
name: flake8
|
||||
entry: poetry run flake8 docling_serve
|
||||
pass_filenames: false
|
||||
language: system
|
||||
files: '\.py$'
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: system
|
||||
name: MyPy
|
||||
entry: poetry run mypy docling_serve
|
||||
entry: uv run --no-sync mypy docling_serve
|
||||
pass_filenames: false
|
||||
language: system
|
||||
files: '\.py$'
|
||||
- repo: local
|
||||
- repo: https://github.com/astral-sh/uv-pre-commit
|
||||
# uv version.
|
||||
rev: 0.6.1
|
||||
hooks:
|
||||
- id: system
|
||||
name: Poetry check
|
||||
entry: poetry check --lock
|
||||
pass_filenames: false
|
||||
language: system
|
||||
- id: uv-lock
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.9.6
|
||||
hooks:
|
||||
# Run the Ruff linter.
|
||||
- id: ruff
|
||||
args: [--exit-non-zero-on-fix, --config=pyproject.toml]
|
||||
# Run the Ruff formatter.
|
||||
# - id: ruff-format
|
||||
# args: [--config=pyproject.toml]
|
||||
|
||||
1
.python-version
Normal file
1
.python-version
Normal file
@@ -0,0 +1 @@
|
||||
3.12
|
||||
11
CHANGELOG.md
Normal file
11
CHANGELOG.md
Normal file
@@ -0,0 +1,11 @@
|
||||
## [v0.3.0](https://github.com/DS4SD/docling-serve/releases/tag/v0.3.0) - 2025-02-19
|
||||
|
||||
### Feature
|
||||
|
||||
* Add new docling-serve cli ([#50](https://github.com/DS4SD/docling-serve/issues/50)) ([`ec33a61`](https://github.com/DS4SD/docling-serve/commit/ec33a61faa7846b9b7998fbf557ebe39a3b800f6))
|
||||
|
||||
### Fix
|
||||
|
||||
* Set DOCLING_SERVE_ARTIFACTS_PATH in images ([#53](https://github.com/DS4SD/docling-serve/issues/53)) ([`4877248`](https://github.com/DS4SD/docling-serve/commit/487724836896576ca4f98e84abf15fd1c383bec8))
|
||||
* Set root UI path when behind proxy ([#38](https://github.com/DS4SD/docling-serve/issues/38)) ([`c64a450`](https://github.com/DS4SD/docling-serve/commit/c64a450bf9ba9947ab180e92bef2763ff710b210))
|
||||
* Support python 3.13 and docling updates and switch to uv ([#48](https://github.com/DS4SD/docling-serve/issues/48)) ([`ae3b490`](https://github.com/DS4SD/docling-serve/commit/ae3b4906f1c0829b1331ea491f3518741cabff71))
|
||||
@@ -2,7 +2,8 @@ ARG BASE_IMAGE=quay.io/sclorg/python-312-c9s:c9s
|
||||
|
||||
FROM ${BASE_IMAGE}
|
||||
|
||||
ARG CPU_ONLY=false
|
||||
ARG MODELS_LIST="layout tableformer picture_classifier easyocr"
|
||||
ARG UV_SYNC_EXTRA_ARGS=""
|
||||
|
||||
USER 0
|
||||
|
||||
@@ -21,6 +22,8 @@ RUN --mount=type=bind,source=os-packages.txt,target=/tmp/os-packages.txt \
|
||||
|
||||
ENV TESSDATA_PREFIX=/usr/share/tesseract/tessdata/
|
||||
|
||||
COPY --from=ghcr.io/astral-sh/uv:0.6.1 /uv /uvx /bin/
|
||||
|
||||
###################################################################################################
|
||||
# Docling layer #
|
||||
###################################################################################################
|
||||
@@ -35,27 +38,25 @@ ENV OMP_NUM_THREADS=4
|
||||
ENV LANG=en_US.UTF-8
|
||||
ENV LC_ALL=en_US.UTF-8
|
||||
ENV PYTHONIOENCODING=utf-8
|
||||
ENV UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy
|
||||
ENV UV_PROJECT_ENVIRONMENT=/opt/app-root
|
||||
|
||||
ENV WITH_UI=True
|
||||
ENV DOCLING_SERVE_ARTIFACTS_PATH=/opt/app-root/src/.cache/docling/models
|
||||
|
||||
COPY --chown=1001:0 pyproject.toml poetry.lock models_download.py README.md ./
|
||||
COPY --chown=1001:0 pyproject.toml uv.lock README.md ./
|
||||
|
||||
RUN pip install --no-cache-dir poetry && \
|
||||
# We already are in a virtual environment, so we don't need to create a new one, only activate it.
|
||||
poetry config virtualenvs.create false && \
|
||||
source /opt/app-root/bin/activate && \
|
||||
if [ "$CPU_ONLY" = "true" ]; then \
|
||||
poetry install --no-root --no-cache --no-interaction --all-extras --with cpu --without dev; \
|
||||
else \
|
||||
poetry install --no-root --no-cache --no-interaction --all-extras --without dev; \
|
||||
fi && \
|
||||
echo "Downloading models..." && \
|
||||
python models_download.py && \
|
||||
chown -R 1001:0 /opt/app-root/src && \
|
||||
chmod -R g=u /opt/app-root/src
|
||||
RUN --mount=type=cache,target=/opt/app-root/src/.cache/uv,uid=1001 \
|
||||
uv sync --frozen --no-install-project --no-dev --all-extras ${UV_SYNC_EXTRA_ARGS} # --no-extra ${NO_EXTRA}
|
||||
|
||||
RUN echo "Downloading models..." && \
|
||||
docling-tools models download -o "${DOCLING_SERVE_ARTIFACTS_PATH}" ${MODELS_LIST} && \
|
||||
chown -R 1001:0 /opt/app-root/src/.cache && \
|
||||
chmod -R g=u /opt/app-root/src/.cache
|
||||
|
||||
COPY --chown=1001:0 --chmod=664 ./docling_serve ./docling_serve
|
||||
RUN --mount=type=cache,target=/opt/app-root/src/.cache/uv,uid=1001 \
|
||||
uv sync --frozen --no-dev --all-extras ${UV_SYNC_EXTRA_ARGS} # --no-extra ${NO_EXTRA}
|
||||
|
||||
EXPOSE 5001
|
||||
|
||||
CMD ["python", "-m", "docling_serve"]
|
||||
CMD ["docling-serve", "run"]
|
||||
|
||||
15
Makefile
15
Makefile
@@ -26,15 +26,15 @@ md-lint-file:
|
||||
|
||||
.PHONY: docling-serve-cpu-image
|
||||
docling-serve-cpu-image: Containerfile ## Build docling-serve "cpu only" container image
|
||||
$(ECHO_PREFIX) printf " %-12s Containerfile\n" "[docling-serve CPU ONLY]"
|
||||
$(CMD_PREFIX) docker build --build-arg CPU_ONLY=true -f Containerfile --platform linux/amd64 -t ghcr.io/ds4sd/docling-serve-cpu:$(TAG) .
|
||||
$(ECHO_PREFIX) printf " %-12s Containerfile\n" "[docling-serve CPU]"
|
||||
$(CMD_PREFIX) docker build --load --build-arg "UV_SYNC_EXTRA_ARGS=--no-extra cu124" -f Containerfile -t ghcr.io/ds4sd/docling-serve-cpu:$(TAG) .
|
||||
$(CMD_PREFIX) docker tag ghcr.io/ds4sd/docling-serve-cpu:$(TAG) ghcr.io/ds4sd/docling-serve-cpu:main
|
||||
$(CMD_PREFIX) docker tag ghcr.io/ds4sd/docling-serve-cpu:$(TAG) quay.io/ds4sd/docling-serve-cpu:main
|
||||
|
||||
.PHONY: docling-serve-gpu-image
|
||||
docling-serve-gpu-image: Containerfile ## Build docling-serve container image with GPU support
|
||||
$(ECHO_PREFIX) printf " %-12s Containerfile\n" "[docling-serve with GPU]"
|
||||
$(CMD_PREFIX) docker build --build-arg CPU_ONLY=false -f Containerfile --platform linux/amd64 -t ghcr.io/ds4sd/docling-serve:$(TAG) .
|
||||
$(CMD_PREFIX) docker build --load --build-arg "UV_SYNC_EXTRA_ARGS=--no-extra cpu" -f Containerfile --platform linux/amd64 -t ghcr.io/ds4sd/docling-serve:$(TAG) .
|
||||
$(CMD_PREFIX) docker tag ghcr.io/ds4sd/docling-serve:$(TAG) ghcr.io/ds4sd/docling-serve:main
|
||||
$(CMD_PREFIX) docker tag ghcr.io/ds4sd/docling-serve:$(TAG) quay.io/ds4sd/docling-serve:main
|
||||
|
||||
@@ -65,13 +65,12 @@ md-lint: .md-lint ## Lint markdown files
|
||||
.PHONY: py-Lint
|
||||
py-lint: ## Lint Python files
|
||||
$(ECHO_PREFIX) printf " %-12s ./...\n" "[PY LINT]"
|
||||
$(CMD_PREFIX) if ! which poetry $(PIPE_DEV_NULL) ; then \
|
||||
echo "Please install poetry." ; \
|
||||
echo "pip install poetry" ; \
|
||||
$(CMD_PREFIX) if ! which uv $(PIPE_DEV_NULL) ; then \
|
||||
echo "Please install uv." ; \
|
||||
exit 1 ; \
|
||||
fi
|
||||
$(CMD_PREFIX) poetry install --all-extras
|
||||
$(CMD_PREFIX) poetry run pre-commit run --all-files
|
||||
$(CMD_PREFIX) uv sync --extra ui
|
||||
$(CMD_PREFIX) uv run pre-commit run --all-files
|
||||
|
||||
.PHONY: run-docling-cpu
|
||||
run-docling-cpu: ## Run the docling-serve container with CPU support and assign a container name
|
||||
|
||||
108
README.md
108
README.md
@@ -293,11 +293,11 @@ The response can be a JSON Document or a File.
|
||||
### CPU only
|
||||
|
||||
```sh
|
||||
# Install poetry if not already available
|
||||
curl -sSL https://install.python-poetry.org | python3 -
|
||||
# Install uv if not already available
|
||||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
|
||||
# Install dependencies
|
||||
poetry install --with cpu
|
||||
uv sync --extra cpu
|
||||
```
|
||||
|
||||
### Cuda GPU
|
||||
@@ -306,29 +306,107 @@ For GPU support use the following command:
|
||||
|
||||
```sh
|
||||
# Install dependencies
|
||||
poetry install
|
||||
uv sync
|
||||
```
|
||||
|
||||
### Gradio UI and different OCR backends
|
||||
|
||||
`/ui` endpoint using `gradio` and different OCR backends can be enabled via package extras:
|
||||
|
||||
```sh
|
||||
# Enable ui and rapidocr
|
||||
uv sync --extra ui --extra rapidocr
|
||||
```
|
||||
|
||||
```sh
|
||||
# Enable tesserocr
|
||||
uv sync --extra tesserocr
|
||||
```
|
||||
|
||||
See `[project.optional-dependencies]` section in `pyproject.toml` for full list of options.
|
||||
|
||||
### Run the server
|
||||
|
||||
The [start_server.sh](./start_server.sh) executable is a convenient script for launching the local webserver.
|
||||
The `docling-serve` executable is a convenient script for launching the webserver both in
|
||||
development and production mode.
|
||||
|
||||
```sh
|
||||
# Run the server
|
||||
bash start_server.sh
|
||||
# Run the server in development mode
|
||||
# - reload is enabled by default
|
||||
# - listening on the 127.0.0.1 address
|
||||
# - ui is enabled by default
|
||||
docling-serve dev
|
||||
|
||||
# Run the server with live reload
|
||||
RELOAD=true bash start_server.sh
|
||||
# Run the server in production mode
|
||||
# - reload is disabled by default
|
||||
# - listening on the 0.0.0.0 address
|
||||
# - ui is disabled by default
|
||||
docling-serve run
|
||||
```
|
||||
|
||||
### Environment variables
|
||||
### Options
|
||||
|
||||
The following variables are available:
|
||||
The `docling-serve` executable allows is controlled with both command line
|
||||
options and environment variables.
|
||||
|
||||
`TESSDATA_PREFIX`: Tesseract data location, example `/usr/share/tesseract/tessdata/`.
|
||||
`UVICORN_WORKERS`: Number of workers to use.
|
||||
`RELOAD`: If `True`, this will enable auto-reload when you modify files, useful for development.
|
||||
`WITH_UI`: If `True`, The Gradio UI will be available at `/ui`.
|
||||
<details>
|
||||
<summary>`docling-serve` help message</summary>
|
||||
|
||||
```sh
|
||||
$ docling-serve dev --help
|
||||
|
||||
Usage: docling-serve dev [OPTIONS]
|
||||
|
||||
Run a Docling Serve app in development mode. 🧪
|
||||
This is equivalent to docling-serve run but with reload
|
||||
enabled and listening on the 127.0.0.1 address.
|
||||
|
||||
Options can be set also with the corresponding ENV variable, with the exception
|
||||
of --enable-ui, --host and --reload.
|
||||
|
||||
╭─ Options ──────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ --host TEXT The host to serve on. For local development in localhost │
|
||||
│ use 127.0.0.1. To enable public access, e.g. in a │
|
||||
│ container, use all the IP addresses available with │
|
||||
│ 0.0.0.0. │
|
||||
│ [default: 127.0.0.1] │
|
||||
│ --port INTEGER The port to serve on. [default: 5001] │
|
||||
│ --reload --no-reload Enable auto-reload of the server when (code) files │
|
||||
│ change. This is resource intensive, use it only during │
|
||||
│ development. │
|
||||
│ [default: reload] │
|
||||
│ --root-path TEXT The root path is used to tell your app that it is being │
|
||||
│ served to the outside world with some path prefix set up │
|
||||
│ in some termination proxy or similar. │
|
||||
│ --proxy-headers --no-proxy-headers Enable/Disable X-Forwarded-Proto, X-Forwarded-For, │
|
||||
│ X-Forwarded-Port to populate remote address info. │
|
||||
│ [default: proxy-headers] │
|
||||
│ --artifacts-path PATH If set to a valid directory, the model weights will be │
|
||||
│ loaded from this path. │
|
||||
│ [default: None] │
|
||||
│ --enable-ui --no-enable-ui Enable the development UI. [default: enable-ui] │
|
||||
│ --help Show this message and exit. │
|
||||
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
#### Environment variables
|
||||
|
||||
The environment variables controlling the `uvicorn` execution can be specified with the `UVICORN_` prefix:
|
||||
|
||||
- `UVICORN_WORKERS`: Number of workers to use.
|
||||
- `UVICORN_RELOAD`: If `True`, this will enable auto-reload when you modify files, useful for development.
|
||||
|
||||
The environment variables controlling specifics of the Docling Serve app can be specified with the
|
||||
`DOCLING_SERVE_` prefix:
|
||||
|
||||
- `DOCLING_SERVE_ARTIFACTS_PATH`: if set Docling will use only the local weights of models, for example `/opt/app-root/src/.cache/docling/models`.
|
||||
- `DOCLING_SERVE_ENABLE_UI`: If `True`, The Gradio UI will be available at `/ui`.
|
||||
|
||||
Others:
|
||||
|
||||
- `TESSDATA_PREFIX`: Tesseract data location, example `/usr/share/tesseract/tessdata/`.
|
||||
|
||||
## Get help and support
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
TESSDATA_PREFIX=/usr/share/tesseract/tessdata/
|
||||
UVICORN_WORKERS=2
|
||||
RELOAD=True
|
||||
UVICORN_RELOAD=True
|
||||
@@ -1,20 +1,302 @@
|
||||
import os
|
||||
import importlib
|
||||
import logging
|
||||
import platform
|
||||
import sys
|
||||
import warnings
|
||||
from pathlib import Path
|
||||
from typing import Annotated, Any, Optional, Union
|
||||
|
||||
from docling_serve.app import app
|
||||
from docling_serve.helper_functions import _str_to_bool
|
||||
import typer
|
||||
import uvicorn
|
||||
from rich.console import Console
|
||||
|
||||
# Launch the FastAPI server
|
||||
if __name__ == "__main__":
|
||||
from uvicorn import run
|
||||
from docling_serve.settings import docling_serve_settings, uvicorn_settings
|
||||
|
||||
port = int(os.getenv("PORT", "5001"))
|
||||
workers = int(os.getenv("UVICORN_WORKERS", "1"))
|
||||
reload = _str_to_bool(os.getenv("RELOAD", "False"))
|
||||
run(
|
||||
app,
|
||||
host="0.0.0.0",
|
||||
port=port,
|
||||
workers=workers,
|
||||
timeout_keep_alive=600,
|
||||
reload=reload,
|
||||
warnings.filterwarnings(action="ignore", category=UserWarning, module="pydantic|torch")
|
||||
warnings.filterwarnings(action="ignore", category=FutureWarning, module="easyocr")
|
||||
|
||||
|
||||
err_console = Console(stderr=True)
|
||||
console = Console()
|
||||
|
||||
app = typer.Typer(
|
||||
no_args_is_help=True,
|
||||
rich_markup_mode="rich",
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def version_callback(value: bool) -> None:
|
||||
if value:
|
||||
docling_serve_version = importlib.metadata.version("docling_serve")
|
||||
docling_version = importlib.metadata.version("docling")
|
||||
docling_core_version = importlib.metadata.version("docling-core")
|
||||
docling_ibm_models_version = importlib.metadata.version("docling-ibm-models")
|
||||
docling_parse_version = importlib.metadata.version("docling-parse")
|
||||
platform_str = platform.platform()
|
||||
py_impl_version = sys.implementation.cache_tag
|
||||
py_lang_version = platform.python_version()
|
||||
console.print(f"Docling Serve version: {docling_serve_version}")
|
||||
console.print(f"Docling version: {docling_version}")
|
||||
console.print(f"Docling Core version: {docling_core_version}")
|
||||
console.print(f"Docling IBM Models version: {docling_ibm_models_version}")
|
||||
console.print(f"Docling Parse version: {docling_parse_version}")
|
||||
console.print(f"Python: {py_impl_version} ({py_lang_version})")
|
||||
console.print(f"Platform: {platform_str}")
|
||||
raise typer.Exit()
|
||||
|
||||
|
||||
@app.callback()
|
||||
def callback(
|
||||
version: Annotated[
|
||||
Union[bool, None],
|
||||
typer.Option(
|
||||
"--version", help="Show the version and exit.", callback=version_callback
|
||||
),
|
||||
] = None,
|
||||
verbose: Annotated[
|
||||
int,
|
||||
typer.Option(
|
||||
"--verbose",
|
||||
"-v",
|
||||
count=True,
|
||||
help="Set the verbosity level. -v for info logging, -vv for debug logging.",
|
||||
),
|
||||
] = 0,
|
||||
) -> None:
|
||||
if verbose == 0:
|
||||
logging.basicConfig(level=logging.WARNING)
|
||||
elif verbose == 1:
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
elif verbose == 2:
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
|
||||
|
||||
def _run(
|
||||
*,
|
||||
command: str,
|
||||
) -> None:
|
||||
server_type = "development" if command == "dev" else "production"
|
||||
|
||||
console.print(f"Starting {server_type} server 🚀")
|
||||
|
||||
url = f"http://{uvicorn_settings.host}:{uvicorn_settings.port}"
|
||||
url_docs = f"{url}/docs"
|
||||
url_ui = f"{url}/ui"
|
||||
|
||||
console.print("")
|
||||
console.print(f"Server started at [link={url}]{url}[/]")
|
||||
console.print(f"Documentation at [link={url_docs}]{url_docs}[/]")
|
||||
if docling_serve_settings.enable_ui:
|
||||
console.print(f"UI at [link={url_ui}]{url_ui}[/]")
|
||||
|
||||
if command == "dev":
|
||||
console.print("")
|
||||
console.print(
|
||||
"Running in development mode, for production use: "
|
||||
"[bold]docling-serve run[/]",
|
||||
)
|
||||
|
||||
console.print("")
|
||||
console.print("Logs:")
|
||||
|
||||
uvicorn.run(
|
||||
app="docling_serve.app:create_app",
|
||||
factory=True,
|
||||
host=uvicorn_settings.host,
|
||||
port=uvicorn_settings.port,
|
||||
reload=uvicorn_settings.reload,
|
||||
workers=uvicorn_settings.workers,
|
||||
root_path=uvicorn_settings.root_path,
|
||||
proxy_headers=uvicorn_settings.proxy_headers,
|
||||
)
|
||||
|
||||
|
||||
@app.command()
|
||||
def dev(
|
||||
*,
|
||||
# uvicorn options
|
||||
host: Annotated[
|
||||
str,
|
||||
typer.Option(
|
||||
help=(
|
||||
"The host to serve on. For local development in localhost "
|
||||
"use [blue]127.0.0.1[/blue]. To enable public access, "
|
||||
"e.g. in a container, use all the IP addresses "
|
||||
"available with [blue]0.0.0.0[/blue]."
|
||||
)
|
||||
),
|
||||
] = "127.0.0.1",
|
||||
port: Annotated[
|
||||
int,
|
||||
typer.Option(help="The port to serve on."),
|
||||
] = uvicorn_settings.port,
|
||||
reload: Annotated[
|
||||
bool,
|
||||
typer.Option(
|
||||
help=(
|
||||
"Enable auto-reload of the server when (code) files change. "
|
||||
"This is [bold]resource intensive[/bold], "
|
||||
"use it only during development."
|
||||
)
|
||||
),
|
||||
] = True,
|
||||
root_path: Annotated[
|
||||
str,
|
||||
typer.Option(
|
||||
help=(
|
||||
"The root path is used to tell your app that it is being served "
|
||||
"to the outside world with some [bold]path prefix[/bold] "
|
||||
"set up in some termination proxy or similar."
|
||||
)
|
||||
),
|
||||
] = uvicorn_settings.root_path,
|
||||
proxy_headers: Annotated[
|
||||
bool,
|
||||
typer.Option(
|
||||
help=(
|
||||
"Enable/Disable X-Forwarded-Proto, X-Forwarded-For, "
|
||||
"X-Forwarded-Port to populate remote address info."
|
||||
)
|
||||
),
|
||||
] = uvicorn_settings.proxy_headers,
|
||||
# docling options
|
||||
artifacts_path: Annotated[
|
||||
Optional[Path],
|
||||
typer.Option(
|
||||
help=(
|
||||
"If set to a valid directory, "
|
||||
"the model weights will be loaded from this path."
|
||||
)
|
||||
),
|
||||
] = docling_serve_settings.artifacts_path,
|
||||
enable_ui: Annotated[bool, typer.Option(help="Enable the development UI.")] = True,
|
||||
) -> Any:
|
||||
"""
|
||||
Run a [bold]Docling Serve[/bold] app in [yellow]development[/yellow] mode. 🧪
|
||||
|
||||
This is equivalent to [bold]docling-serve run[/bold] but with [bold]reload[/bold]
|
||||
enabled and listening on the [blue]127.0.0.1[/blue] address.
|
||||
|
||||
Options can be set also with the corresponding ENV variable, with the exception
|
||||
of --enable-ui, --host and --reload.
|
||||
"""
|
||||
|
||||
uvicorn_settings.host = host
|
||||
uvicorn_settings.port = port
|
||||
uvicorn_settings.reload = reload
|
||||
uvicorn_settings.root_path = root_path
|
||||
uvicorn_settings.proxy_headers = proxy_headers
|
||||
|
||||
docling_serve_settings.artifacts_path = artifacts_path
|
||||
docling_serve_settings.enable_ui = enable_ui
|
||||
|
||||
_run(
|
||||
command="dev",
|
||||
)
|
||||
|
||||
|
||||
@app.command()
|
||||
def run(
|
||||
*,
|
||||
host: Annotated[
|
||||
str,
|
||||
typer.Option(
|
||||
help=(
|
||||
"The host to serve on. For local development in localhost "
|
||||
"use [blue]127.0.0.1[/blue]. To enable public access, "
|
||||
"e.g. in a container, use all the IP addresses "
|
||||
"available with [blue]0.0.0.0[/blue]."
|
||||
)
|
||||
),
|
||||
] = uvicorn_settings.host,
|
||||
port: Annotated[
|
||||
int,
|
||||
typer.Option(help="The port to serve on."),
|
||||
] = uvicorn_settings.port,
|
||||
reload: Annotated[
|
||||
bool,
|
||||
typer.Option(
|
||||
help=(
|
||||
"Enable auto-reload of the server when (code) files change. "
|
||||
"This is [bold]resource intensive[/bold], "
|
||||
"use it only during development."
|
||||
)
|
||||
),
|
||||
] = uvicorn_settings.reload,
|
||||
workers: Annotated[
|
||||
Union[int, None],
|
||||
typer.Option(
|
||||
help=(
|
||||
"Use multiple worker processes. "
|
||||
"Mutually exclusive with the --reload flag."
|
||||
)
|
||||
),
|
||||
] = uvicorn_settings.workers,
|
||||
root_path: Annotated[
|
||||
str,
|
||||
typer.Option(
|
||||
help=(
|
||||
"The root path is used to tell your app that it is being served "
|
||||
"to the outside world with some [bold]path prefix[/bold] "
|
||||
"set up in some termination proxy or similar."
|
||||
)
|
||||
),
|
||||
] = uvicorn_settings.root_path,
|
||||
proxy_headers: Annotated[
|
||||
bool,
|
||||
typer.Option(
|
||||
help=(
|
||||
"Enable/Disable X-Forwarded-Proto, X-Forwarded-For, "
|
||||
"X-Forwarded-Port to populate remote address info."
|
||||
)
|
||||
),
|
||||
] = uvicorn_settings.proxy_headers,
|
||||
# docling options
|
||||
artifacts_path: Annotated[
|
||||
Optional[Path],
|
||||
typer.Option(
|
||||
help=(
|
||||
"If set to a valid directory, "
|
||||
"the model weights will be loaded from this path."
|
||||
)
|
||||
),
|
||||
] = docling_serve_settings.artifacts_path,
|
||||
enable_ui: Annotated[
|
||||
bool, typer.Option(help="Enable the development UI.")
|
||||
] = docling_serve_settings.enable_ui,
|
||||
) -> Any:
|
||||
"""
|
||||
Run a [bold]Docling Serve[/bold] app in [green]production[/green] mode. 🚀
|
||||
|
||||
This is equivalent to [bold]docling-serve dev[/bold] but with [bold]reload[/bold]
|
||||
disabled and listening on the [blue]0.0.0.0[/blue] address.
|
||||
|
||||
Options can be set also with the corresponding ENV variable, e.g. UVICORN_PORT
|
||||
or DOCLING_SERVE_ENABLE_UI.
|
||||
"""
|
||||
|
||||
uvicorn_settings.host = host
|
||||
uvicorn_settings.port = port
|
||||
uvicorn_settings.reload = reload
|
||||
uvicorn_settings.workers = workers
|
||||
uvicorn_settings.root_path = root_path
|
||||
uvicorn_settings.proxy_headers = proxy_headers
|
||||
|
||||
docling_serve_settings.artifacts_path = artifacts_path
|
||||
docling_serve_settings.enable_ui = enable_ui
|
||||
|
||||
_run(
|
||||
command="run",
|
||||
)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
app()
|
||||
|
||||
|
||||
# Launch the CLI when calling python -m docling_serve
|
||||
if __name__ == "__main__":
|
||||
|
||||
main()
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import logging
|
||||
import os
|
||||
import tempfile
|
||||
from contextlib import asynccontextmanager
|
||||
from io import BytesIO
|
||||
@@ -8,7 +7,6 @@ from typing import Annotated, Any, Dict, List, Optional, Union
|
||||
|
||||
from docling.datamodel.base_models import DocumentStream, InputFormat
|
||||
from docling.document_converter import DocumentConverter
|
||||
from dotenv import load_dotenv
|
||||
from fastapi import BackgroundTasks, FastAPI, UploadFile
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.responses import RedirectResponse
|
||||
@@ -22,17 +20,9 @@ from docling_serve.docling_conversion import (
|
||||
converters,
|
||||
get_pdf_pipeline_opts,
|
||||
)
|
||||
from docling_serve.helper_functions import FormDepends, _str_to_bool
|
||||
from docling_serve.helper_functions import FormDepends
|
||||
from docling_serve.response_preparation import ConvertDocumentResponse, process_results
|
||||
|
||||
# Load local env vars if present
|
||||
load_dotenv()
|
||||
|
||||
WITH_UI = _str_to_bool(os.getenv("WITH_UI", "False"))
|
||||
if WITH_UI:
|
||||
import gradio as gr
|
||||
|
||||
from docling_serve.gradio_ui import ui as gradio_ui
|
||||
from docling_serve.settings import docling_serve_settings
|
||||
|
||||
|
||||
# Set up custom logging as we'll be intermixes with FastAPI/Uvicorn's logging
|
||||
@@ -70,7 +60,6 @@ _log = logging.getLogger(__name__)
|
||||
# Context manager to initialize and clean up the lifespan of the FastAPI app
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
# settings = Settings()
|
||||
|
||||
# Converter with default options
|
||||
pdf_format_option, options_hash = get_pdf_pipeline_opts(ConvertDocumentsOptions())
|
||||
@@ -86,139 +75,156 @@ async def lifespan(app: FastAPI):
|
||||
yield
|
||||
|
||||
converters.clear()
|
||||
if WITH_UI:
|
||||
gradio_ui.close()
|
||||
# if WITH_UI:
|
||||
# gradio_ui.close()
|
||||
|
||||
|
||||
##################################
|
||||
# App creation and configuration #
|
||||
##################################
|
||||
|
||||
app = FastAPI(
|
||||
title="Docling Serve",
|
||||
lifespan=lifespan,
|
||||
)
|
||||
|
||||
origins = ["*"]
|
||||
methods = ["*"]
|
||||
headers = ["*"]
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=origins,
|
||||
allow_credentials=True,
|
||||
allow_methods=methods,
|
||||
allow_headers=headers,
|
||||
)
|
||||
|
||||
# Mount the Gradio app
|
||||
if WITH_UI:
|
||||
tmp_output_dir = Path(tempfile.mkdtemp())
|
||||
gradio_ui.gradio_output_dir = tmp_output_dir
|
||||
app = gr.mount_gradio_app(
|
||||
app, gradio_ui, path="/ui", allowed_paths=["./logo.png", tmp_output_dir]
|
||||
def create_app():
|
||||
app = FastAPI(
|
||||
title="Docling Serve",
|
||||
lifespan=lifespan,
|
||||
)
|
||||
|
||||
origins = ["*"]
|
||||
methods = ["*"]
|
||||
headers = ["*"]
|
||||
|
||||
#############################
|
||||
# API Endpoints definitions #
|
||||
#############################
|
||||
|
||||
|
||||
# Favicon
|
||||
@app.get("/favicon.ico", include_in_schema=False)
|
||||
async def favicon():
|
||||
response = RedirectResponse(url="https://ds4sd.github.io/docling/assets/logo.png")
|
||||
return response
|
||||
|
||||
|
||||
# Status
|
||||
class HealthCheckResponse(BaseModel):
|
||||
status: str = "ok"
|
||||
|
||||
|
||||
@app.get("/health")
|
||||
def health() -> HealthCheckResponse:
|
||||
return HealthCheckResponse()
|
||||
|
||||
|
||||
# API readiness compatibility for OpenShift AI Workbench
|
||||
@app.get("/api", include_in_schema=False)
|
||||
def api_check() -> HealthCheckResponse:
|
||||
return HealthCheckResponse()
|
||||
|
||||
|
||||
# Convert a document from URL(s)
|
||||
@app.post(
|
||||
"/v1alpha/convert/source",
|
||||
response_model=ConvertDocumentResponse,
|
||||
responses={
|
||||
200: {
|
||||
"content": {"application/zip": {}},
|
||||
# "description": "Return the JSON item or an image.",
|
||||
}
|
||||
},
|
||||
)
|
||||
def process_url(
|
||||
background_tasks: BackgroundTasks, conversion_request: ConvertDocumentsRequest
|
||||
):
|
||||
sources: List[Union[str, DocumentStream]] = []
|
||||
headers: Optional[Dict[str, Any]] = None
|
||||
if isinstance(conversion_request, ConvertDocumentFileSourcesRequest):
|
||||
for file_source in conversion_request.file_sources:
|
||||
sources.append(file_source.to_document_stream())
|
||||
else:
|
||||
for http_source in conversion_request.http_sources:
|
||||
sources.append(http_source.url)
|
||||
if headers is None and http_source.headers:
|
||||
headers = http_source.headers
|
||||
|
||||
# Note: results are only an iterator->lazy evaluation
|
||||
results = convert_documents(
|
||||
sources=sources, options=conversion_request.options, headers=headers
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=origins,
|
||||
allow_credentials=True,
|
||||
allow_methods=methods,
|
||||
allow_headers=headers,
|
||||
)
|
||||
|
||||
# The real processing will happen here
|
||||
response = process_results(
|
||||
background_tasks=background_tasks,
|
||||
conversion_options=conversion_request.options,
|
||||
conv_results=results,
|
||||
# Mount the Gradio app
|
||||
if docling_serve_settings.enable_ui:
|
||||
|
||||
try:
|
||||
import gradio as gr
|
||||
|
||||
from docling_serve.gradio_ui import ui as gradio_ui
|
||||
|
||||
tmp_output_dir = Path(tempfile.mkdtemp())
|
||||
gradio_ui.gradio_output_dir = tmp_output_dir
|
||||
app = gr.mount_gradio_app(
|
||||
app,
|
||||
gradio_ui,
|
||||
path="/ui",
|
||||
allowed_paths=["./logo.png", tmp_output_dir],
|
||||
root_path="/ui",
|
||||
)
|
||||
except ImportError:
|
||||
_log.warning(
|
||||
"Docling Serve enable_ui is activated, but gradio is not installed. "
|
||||
"Install it with `pip install docling-serve[ui]` "
|
||||
"or `pip install gradio`"
|
||||
)
|
||||
|
||||
#############################
|
||||
# API Endpoints definitions #
|
||||
#############################
|
||||
|
||||
# Favicon
|
||||
@app.get("/favicon.ico", include_in_schema=False)
|
||||
async def favicon():
|
||||
response = RedirectResponse(
|
||||
url="https://ds4sd.github.io/docling/assets/logo.png"
|
||||
)
|
||||
return response
|
||||
|
||||
# Status
|
||||
class HealthCheckResponse(BaseModel):
|
||||
status: str = "ok"
|
||||
|
||||
@app.get("/health")
|
||||
def health() -> HealthCheckResponse:
|
||||
return HealthCheckResponse()
|
||||
|
||||
# API readiness compatibility for OpenShift AI Workbench
|
||||
@app.get("/api", include_in_schema=False)
|
||||
def api_check() -> HealthCheckResponse:
|
||||
return HealthCheckResponse()
|
||||
|
||||
# Convert a document from URL(s)
|
||||
@app.post(
|
||||
"/v1alpha/convert/source",
|
||||
response_model=ConvertDocumentResponse,
|
||||
responses={
|
||||
200: {
|
||||
"content": {"application/zip": {}},
|
||||
# "description": "Return the JSON item or an image.",
|
||||
}
|
||||
},
|
||||
)
|
||||
def process_url(
|
||||
background_tasks: BackgroundTasks, conversion_request: ConvertDocumentsRequest
|
||||
):
|
||||
sources: List[Union[str, DocumentStream]] = []
|
||||
headers: Optional[Dict[str, Any]] = None
|
||||
if isinstance(conversion_request, ConvertDocumentFileSourcesRequest):
|
||||
for file_source in conversion_request.file_sources:
|
||||
sources.append(file_source.to_document_stream())
|
||||
else:
|
||||
for http_source in conversion_request.http_sources:
|
||||
sources.append(http_source.url)
|
||||
if headers is None and http_source.headers:
|
||||
headers = http_source.headers
|
||||
|
||||
return response
|
||||
# Note: results are only an iterator->lazy evaluation
|
||||
results = convert_documents(
|
||||
sources=sources, options=conversion_request.options, headers=headers
|
||||
)
|
||||
|
||||
# The real processing will happen here
|
||||
response = process_results(
|
||||
background_tasks=background_tasks,
|
||||
conversion_options=conversion_request.options,
|
||||
conv_results=results,
|
||||
)
|
||||
|
||||
# Convert a document from file(s)
|
||||
@app.post(
|
||||
"/v1alpha/convert/file",
|
||||
response_model=ConvertDocumentResponse,
|
||||
responses={
|
||||
200: {
|
||||
"content": {"application/zip": {}},
|
||||
}
|
||||
},
|
||||
)
|
||||
async def process_file(
|
||||
background_tasks: BackgroundTasks,
|
||||
files: List[UploadFile],
|
||||
options: Annotated[ConvertDocumentsOptions, FormDepends(ConvertDocumentsOptions)],
|
||||
):
|
||||
return response
|
||||
|
||||
_log.info(f"Received {len(files)} files for processing.")
|
||||
|
||||
# Load the uploaded files to Docling DocumentStream
|
||||
file_sources = []
|
||||
for file in files:
|
||||
buf = BytesIO(file.file.read())
|
||||
name = file.filename if file.filename else "file.pdf"
|
||||
file_sources.append(DocumentStream(name=name, stream=buf))
|
||||
|
||||
results = convert_documents(sources=file_sources, options=options)
|
||||
|
||||
response = process_results(
|
||||
background_tasks=background_tasks,
|
||||
conversion_options=options,
|
||||
conv_results=results,
|
||||
# Convert a document from file(s)
|
||||
@app.post(
|
||||
"/v1alpha/convert/file",
|
||||
response_model=ConvertDocumentResponse,
|
||||
responses={
|
||||
200: {
|
||||
"content": {"application/zip": {}},
|
||||
}
|
||||
},
|
||||
)
|
||||
async def process_file(
|
||||
background_tasks: BackgroundTasks,
|
||||
files: List[UploadFile],
|
||||
options: Annotated[
|
||||
ConvertDocumentsOptions, FormDepends(ConvertDocumentsOptions)
|
||||
],
|
||||
):
|
||||
|
||||
return response
|
||||
_log.info(f"Received {len(files)} files for processing.")
|
||||
|
||||
# Load the uploaded files to Docling DocumentStream
|
||||
file_sources = []
|
||||
for file in files:
|
||||
buf = BytesIO(file.file.read())
|
||||
name = file.filename if file.filename else "file.pdf"
|
||||
file_sources.append(DocumentStream(name=name, stream=buf))
|
||||
|
||||
results = convert_documents(sources=file_sources, options=options)
|
||||
|
||||
response = process_results(
|
||||
background_tasks=background_tasks,
|
||||
conversion_options=options,
|
||||
conv_results=results,
|
||||
)
|
||||
|
||||
return response
|
||||
|
||||
return app
|
||||
|
||||
@@ -39,6 +39,7 @@ from fastapi import HTTPException
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from docling_serve.helper_functions import _to_list_of_strings
|
||||
from docling_serve.settings import docling_serve_settings
|
||||
|
||||
_log = logging.getLogger(__name__)
|
||||
|
||||
@@ -55,7 +56,7 @@ class ConvertDocumentsOptions(BaseModel):
|
||||
),
|
||||
examples=[[v.value for v in InputFormat]],
|
||||
),
|
||||
] = [v for v in InputFormat]
|
||||
] = list(InputFormat)
|
||||
|
||||
to_formats: Annotated[
|
||||
List[OutputFormat],
|
||||
@@ -161,7 +162,7 @@ class ConvertDocumentsOptions(BaseModel):
|
||||
bool,
|
||||
Field(
|
||||
description=(
|
||||
"Abort on error if enabled. " "Boolean. Optional, defaults to false."
|
||||
"Abort on error if enabled. Boolean. Optional, defaults to false."
|
||||
),
|
||||
# examples=[False],
|
||||
),
|
||||
@@ -264,7 +265,7 @@ ConvertDocumentsRequest = Union[
|
||||
|
||||
|
||||
# Document converters will be preloaded and stored in a dictionary
|
||||
converters: Dict[str, DocumentConverter] = {}
|
||||
converters: Dict[bytes, DocumentConverter] = {}
|
||||
|
||||
|
||||
# Custom serializer for PdfFormatOption
|
||||
@@ -276,6 +277,11 @@ def _serialize_pdf_format_option(pdf_format_option: PdfFormatOption) -> str:
|
||||
if pdf_format_option.pipeline_options:
|
||||
data["pipeline_options"] = pdf_format_option.pipeline_options.model_dump()
|
||||
|
||||
# Replace `artifacts_path` with a string representation
|
||||
data["pipeline_options"]["artifacts_path"] = repr(
|
||||
data["pipeline_options"]["artifacts_path"]
|
||||
)
|
||||
|
||||
# Replace `pipeline_cls` with a string representation
|
||||
data["pipeline_cls"] = repr(data["pipeline_cls"])
|
||||
|
||||
@@ -293,10 +299,9 @@ def _serialize_pdf_format_option(pdf_format_option: PdfFormatOption) -> str:
|
||||
|
||||
|
||||
# Computes the PDF pipeline options and returns the PdfFormatOption and its hash
|
||||
def get_pdf_pipeline_opts(
|
||||
def get_pdf_pipeline_opts( # noqa: C901
|
||||
request: ConvertDocumentsOptions,
|
||||
) -> Tuple[PdfFormatOption, str]:
|
||||
|
||||
) -> Tuple[PdfFormatOption, bytes]:
|
||||
if request.ocr_engine == OcrEngine.EASYOCR:
|
||||
try:
|
||||
import easyocr # noqa: F401
|
||||
@@ -364,6 +369,31 @@ def get_pdf_pipeline_opts(
|
||||
else:
|
||||
raise RuntimeError(f"Unexpected PDF backend type {request.pdf_backend}")
|
||||
|
||||
if docling_serve_settings.artifacts_path is not None:
|
||||
if str(docling_serve_settings.artifacts_path.absolute()) == "":
|
||||
_log.info(
|
||||
"artifacts_path is an empty path, model weights will be dowloaded "
|
||||
"at runtime."
|
||||
)
|
||||
pipeline_options.artifacts_path = None
|
||||
elif docling_serve_settings.artifacts_path.is_dir():
|
||||
_log.info(
|
||||
"artifacts_path is set to a valid directory. "
|
||||
"No model weights will be downloaded at runtime."
|
||||
)
|
||||
pipeline_options.artifacts_path = docling_serve_settings.artifacts_path
|
||||
else:
|
||||
_log.warning(
|
||||
"artifacts_path is set to an invalid directory. "
|
||||
"The system will download the model weights at runtime."
|
||||
)
|
||||
pipeline_options.artifacts_path = None
|
||||
else:
|
||||
_log.info(
|
||||
"artifacts_path is unset. "
|
||||
"The system will download the model weights at runtime."
|
||||
)
|
||||
|
||||
pdf_format_option = PdfFormatOption(
|
||||
pipeline_options=pipeline_options,
|
||||
backend=backend,
|
||||
@@ -371,7 +401,7 @@ def get_pdf_pipeline_opts(
|
||||
|
||||
serialized_data = _serialize_pdf_format_option(pdf_format_option)
|
||||
|
||||
options_hash = hashlib.sha1(serialized_data.encode()).hexdigest()
|
||||
options_hash = hashlib.sha1(serialized_data.encode()).digest()
|
||||
|
||||
return pdf_format_option, options_hash
|
||||
|
||||
|
||||
@@ -1,6 +1,33 @@
|
||||
from pathlib import Path
|
||||
from typing import Optional, Union
|
||||
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
class UvicornSettings(BaseSettings):
|
||||
model_config = SettingsConfigDict(
|
||||
env_prefix="UVICORN_", env_file=".env", extra="allow"
|
||||
)
|
||||
|
||||
model_config = SettingsConfigDict(env_prefix="DOCLING_")
|
||||
host: str = "0.0.0.0"
|
||||
port: int = 5001
|
||||
reload: bool = False
|
||||
root_path: str = ""
|
||||
proxy_headers: bool = True
|
||||
workers: Union[int, None] = None
|
||||
|
||||
|
||||
class DoclingServeSettings(BaseSettings):
|
||||
model_config = SettingsConfigDict(
|
||||
env_prefix="DOCLING_SERVE_",
|
||||
env_file=".env",
|
||||
env_parse_none_str="",
|
||||
extra="allow",
|
||||
)
|
||||
|
||||
enable_ui: bool = False
|
||||
artifacts_path: Optional[Path] = None
|
||||
|
||||
|
||||
uvicorn_settings = UvicornSettings()
|
||||
docling_serve_settings = DoclingServeSettings()
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
import os
|
||||
import zipfile
|
||||
|
||||
import requests
|
||||
from deepsearch_glm.utils.load_pretrained_models import load_pretrained_nlp_models
|
||||
from docling.pipeline.standard_pdf_pipeline import StandardPdfPipeline
|
||||
|
||||
# Download Docling models
|
||||
StandardPdfPipeline.download_models_hf(force=True)
|
||||
load_pretrained_nlp_models(verbose=True)
|
||||
|
||||
# Download EasyOCR models
|
||||
urls = [
|
||||
"https://github.com/JaidedAI/EasyOCR/releases/download/v1.3/latin_g2.zip",
|
||||
"https://github.com/JaidedAI/EasyOCR/releases/download/pre-v1.1.6/craft_mlt_25k.zip"
|
||||
]
|
||||
|
||||
local_zip_paths = [
|
||||
"/opt/app-root/src/latin_g2.zip",
|
||||
"/opt/app-root/src/craft_mlt_25k.zip"
|
||||
]
|
||||
|
||||
extract_path = "/opt/app-root/src/.EasyOCR/model/"
|
||||
|
||||
for url, local_zip_path in zip(urls, local_zip_paths):
|
||||
# Download the file
|
||||
response = requests.get(url)
|
||||
with open(local_zip_path, "wb") as file:
|
||||
file.write(response.content)
|
||||
|
||||
# Unzip the file
|
||||
with zipfile.ZipFile(local_zip_path, "r") as zip_ref:
|
||||
zip_ref.extractall(extract_path)
|
||||
|
||||
# Clean up the zip file
|
||||
os.remove(local_zip_path)
|
||||
4699
poetry.lock
generated
4699
poetry.lock
generated
File diff suppressed because it is too large
Load Diff
236
pyproject.toml
236
pyproject.toml
@@ -1,25 +1,25 @@
|
||||
[tool.poetry]
|
||||
[project]
|
||||
name = "docling-serve"
|
||||
version = "0.2.0"
|
||||
version = "0.3.0" # DO NOT EDIT, updated automatically
|
||||
description = "Running Docling as a service"
|
||||
license = "MIT"
|
||||
license = {text = "MIT"}
|
||||
authors = [
|
||||
"Michele Dolfi <dol@zurich.ibm.com>",
|
||||
"Christoph Auer <cau@zurich.ibm.com>",
|
||||
"Panos Vagenas <pva@zurich.ibm.com>",
|
||||
"Cesar Berrospi Ramis <ceb@zurich.ibm.com>",
|
||||
"Peter Staar <taa@zurich.ibm.com>",
|
||||
{name="Michele Dolfi", email="dol@zurich.ibm.com"},
|
||||
{name="Guillaume Moutier", email="gmoutier@redhat.com"},
|
||||
{name="Anil Vishnoi", email="avishnoi@redhat.com"},
|
||||
{name="Panos Vagenas", email="pva@zurich.ibm.com"},
|
||||
{name="Panos Vagenas", email="pva@zurich.ibm.com"},
|
||||
{name="Christoph Auer", email="cau@zurich.ibm.com"},
|
||||
{name="Peter Staar", email="taa@zurich.ibm.com"},
|
||||
]
|
||||
maintainers = [
|
||||
"Peter Staar <taa@zurich.ibm.com>",
|
||||
"Christoph Auer <cau@zurich.ibm.com>",
|
||||
"Michele Dolfi <dol@zurich.ibm.com>",
|
||||
"Cesar Berrospi Ramis <ceb@zurich.ibm.com>",
|
||||
"Panos Vagenas <pva@zurich.ibm.com>",
|
||||
{name="Michele Dolfi", email="dol@zurich.ibm.com"},
|
||||
{name="Anil Vishnoi", email="avishnoi@redhat.com"},
|
||||
{name="Panos Vagenas", email="pva@zurich.ibm.com"},
|
||||
{name="Christoph Auer", email="cau@zurich.ibm.com"},
|
||||
{name="Peter Staar", email="taa@zurich.ibm.com"},
|
||||
]
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/DS4SD/docling-serve"
|
||||
homepage = "https://github.com/DS4SD/docling-serve"
|
||||
classifiers = [
|
||||
"License :: OSI Approved :: MIT License",
|
||||
"Operating System :: OS Independent",
|
||||
@@ -28,96 +28,145 @@ classifiers = [
|
||||
"Typing :: Typed",
|
||||
"Programming Language :: Python :: 3"
|
||||
]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = ">=3.10,<3.13" # 3.10 needed for Gradio, and no torchvision build for 3.13 yet
|
||||
docling = "^2.14.0"
|
||||
fastapi = {version = "^0.115.6", extras = ["standard"]}
|
||||
gradio = { version = "^5.9.1", optional = true }
|
||||
uvicorn = "~0.29.0"
|
||||
pydantic = "^2.10.3"
|
||||
pydantic-settings = "^2.4.0"
|
||||
python-multipart = "^0.0.19"
|
||||
httpx = "^0.28.1"
|
||||
tesserocr = { version = "^2.7.1", optional = true }
|
||||
rapidocr-onnxruntime = { version = "^1.4.0", optional = true, markers = "python_version < '3.13'" }
|
||||
onnxruntime = [
|
||||
# 1.19.2 is the last version with python3.9 support,
|
||||
# see https://github.com/microsoft/onnxruntime/releases/tag/v1.20.0
|
||||
{ version = ">=1.7.0,<1.20.0", optional = true, markers = "python_version < '3.10'" },
|
||||
{ version = "^1.7.0", optional = true, markers = "python_version >= '3.10'" }
|
||||
requires-python = ">=3.10"
|
||||
dependencies = [
|
||||
"docling~=2.23",
|
||||
"fastapi[standard]~=0.115",
|
||||
"httpx~=0.28",
|
||||
"pydantic~=2.10",
|
||||
"pydantic-settings~=2.4",
|
||||
"python-multipart>=0.0.14,<0.1.0",
|
||||
"typer~=0.12",
|
||||
"uvicorn[standard]>=0.29.0,<1.0.0",
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
ui = [
|
||||
"gradio~=5.9"
|
||||
]
|
||||
tesserocr = [
|
||||
"tesserocr~=2.7"
|
||||
]
|
||||
rapidocr = [
|
||||
"rapidocr-onnxruntime~=1.4; python_version<'3.13'",
|
||||
"onnxruntime~=1.7",
|
||||
]
|
||||
cpu = [
|
||||
"torch>=2.6.0",
|
||||
"torchvision>=0.21.0",
|
||||
]
|
||||
cu124 = [
|
||||
"torch>=2.6.0",
|
||||
"torchvision>=0.21.0",
|
||||
]
|
||||
|
||||
[tool.poetry.extras]
|
||||
ui = ["gradio"]
|
||||
tesserocr = ["tesserocr"]
|
||||
rapidocr = ["rapidocr-onnxruntime", "onnxruntime"]
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"mypy~=1.11",
|
||||
"pre-commit~=3.8",
|
||||
"pytest~=8.3",
|
||||
"pytest-asyncio~=0.24",
|
||||
"pytest-check~=2.4",
|
||||
"python-semantic-release~=7.32",
|
||||
"ruff>=0.9.6",
|
||||
]
|
||||
|
||||
[tool.uv]
|
||||
package = true
|
||||
conflicts = [
|
||||
[
|
||||
{ extra = "cpu" },
|
||||
{ extra = "cu124" },
|
||||
],
|
||||
]
|
||||
|
||||
[tool.poetry.group.pypi-torch]
|
||||
optional = false
|
||||
|
||||
[tool.poetry.group.pypi-torch.dependencies]
|
||||
[tool.uv.sources]
|
||||
torch = [
|
||||
{version = "!=2.4.1+cpu" },
|
||||
{ index = "pytorch-cpu", extra = "cpu" },
|
||||
{ index = "pytorch-cu124", extra = "cu124" },
|
||||
]
|
||||
torchvision = [
|
||||
{version = "!=0.19.1+cpu" },
|
||||
{ index = "pytorch-cpu", extra = "cpu" },
|
||||
{ index = "pytorch-cu124", extra = "cu124" },
|
||||
]
|
||||
|
||||
[tool.poetry.group.cpu]
|
||||
optional = true
|
||||
[[tool.uv.index]]
|
||||
name = "pytorch-cpu"
|
||||
url = "https://download.pytorch.org/whl/cpu"
|
||||
explicit = true
|
||||
|
||||
[tool.poetry.group.cpu.dependencies]
|
||||
torch = [
|
||||
{markers = 'platform_machine=="x86_64" and sys_platform=="linux" and python_version == "3.10"', url="https://download.pytorch.org/whl/cpu/torch-2.4.1%2Bcpu-cp310-cp310-linux_x86_64.whl"},
|
||||
{markers = 'platform_machine=="x86_64" and sys_platform=="linux" and python_version == "3.11"', url="https://download.pytorch.org/whl/cpu/torch-2.4.1%2Bcpu-cp311-cp311-linux_x86_64.whl"},
|
||||
{markers = 'platform_machine=="x86_64" and sys_platform=="linux" and python_version == "3.12"', url="https://download.pytorch.org/whl/cpu/torch-2.4.1%2Bcpu-cp312-cp312-linux_x86_64.whl"},
|
||||
]
|
||||
torchvision = [
|
||||
{markers = 'platform_machine=="x86_64" and sys_platform=="linux" and python_version == "3.10"', url="https://download.pytorch.org/whl/cpu/torchvision-0.19.1%2Bcpu-cp310-cp310-linux_x86_64.whl"},
|
||||
{markers = 'platform_machine=="x86_64" and sys_platform=="linux" and python_version == "3.11"', url="https://download.pytorch.org/whl/cpu/torchvision-0.19.1%2Bcpu-cp311-cp311-linux_x86_64.whl"},
|
||||
{markers = 'platform_machine=="x86_64" and sys_platform=="linux" and python_version == "3.12"', url="https://download.pytorch.org/whl/cpu/torchvision-0.19.1%2Bcpu-cp312-cp312-linux_x86_64.whl"},
|
||||
]
|
||||
[[tool.uv.index]]
|
||||
name = "pytorch-cu124"
|
||||
url = "https://download.pytorch.org/whl/cu124"
|
||||
explicit = true
|
||||
|
||||
[tool.poetry.group.constraints.dependencies]
|
||||
numpy = [
|
||||
{ version = "^2.1.0", markers = 'python_version >= "3.13"' },
|
||||
{ version = "^1.24.4", markers = 'python_version < "3.13"' },
|
||||
]
|
||||
[tool.setuptools.packages.find]
|
||||
include = ["docling_serve"]
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
black = "^24.8.0"
|
||||
isort = "^5.13.2"
|
||||
pre-commit = "^3.8.0"
|
||||
autoflake = "^2.3.1"
|
||||
flake8 = "^7.1.1"
|
||||
pytest = "^8.3.4"
|
||||
pytest-asyncio = "^0.24.0"
|
||||
pytest-check = "^2.4.1"
|
||||
mypy = "^1.11.2"
|
||||
[project.scripts]
|
||||
docling-serve = "docling_serve.__main__:main"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
[project.urls]
|
||||
Homepage = "https://github.com/DS4SD/docling-serve"
|
||||
# Documentation = "https://ds4sd.github.io/docling"
|
||||
Repository = "https://github.com/DS4SD/docling-serve"
|
||||
Issues = "https://github.com/DS4SD/docling-serve/issues"
|
||||
Changelog = "https://github.com/DS4SD/docling-serve/blob/main/CHANGELOG.md"
|
||||
|
||||
[tool.black]
|
||||
[tool.ruff]
|
||||
target-version = "py310"
|
||||
line-length = 88
|
||||
target-version = ["py310"]
|
||||
include = '\.pyi?$'
|
||||
respect-gitignore = true
|
||||
|
||||
[tool.isort]
|
||||
profile = "black"
|
||||
line_length = 88
|
||||
py_version=311
|
||||
# extend-exclude = [
|
||||
# "tests",
|
||||
# ]
|
||||
|
||||
[tool.autoflake]
|
||||
in-place = true
|
||||
remove-all-unused-imports = true
|
||||
remove-unused-variables = true
|
||||
expand-star-imports = true
|
||||
recursive = true
|
||||
[tool.ruff.format]
|
||||
skip-magic-trailing-comma = false
|
||||
|
||||
[tool.ruff.lint]
|
||||
select = [
|
||||
# "B", # flake8-bugbear
|
||||
"C", # flake8-comprehensions
|
||||
"C9", # mccabe
|
||||
# "D", # flake8-docstrings
|
||||
"E", # pycodestyle errors (default)
|
||||
"F", # pyflakes (default)
|
||||
"I", # isort
|
||||
"PD", # pandas-vet
|
||||
"PIE", # pie
|
||||
# "PTH", # pathlib
|
||||
"Q", # flake8-quotes
|
||||
# "RET", # return
|
||||
"RUF", # Enable all ruff-specific checks
|
||||
# "SIM", # simplify
|
||||
"S307", # eval
|
||||
# "T20", # (disallow print statements) keep debugging statements out of the codebase
|
||||
"W", # pycodestyle warnings
|
||||
"ASYNC" # async
|
||||
]
|
||||
|
||||
ignore = [
|
||||
"E501", # Line too long, handled by ruff formatter
|
||||
"D107", # "Missing docstring in __init__",
|
||||
"F811", # "redefinition of the same function"
|
||||
"PL", # Pylint
|
||||
"RUF012", # Mutable Class Attributes
|
||||
]
|
||||
|
||||
#extend-select = []
|
||||
|
||||
[tool.ruff.lint.per-file-ignores]
|
||||
"__init__.py" = ["E402", "F401"]
|
||||
"tests/*.py" = ["ASYNC"] # Disable ASYNC check for tests
|
||||
|
||||
[tool.ruff.lint.mccabe]
|
||||
max-complexity = 15
|
||||
|
||||
[tool.ruff.lint.isort]
|
||||
combine-as-imports = true
|
||||
known-third-party = ["docling", "docling_core"]
|
||||
|
||||
[tool.mypy]
|
||||
pretty = true
|
||||
@@ -150,3 +199,16 @@ addopts = "-rA --color=yes --tb=short --maxfail=5"
|
||||
markers = [
|
||||
"asyncio",
|
||||
]
|
||||
|
||||
[tool.semantic_release]
|
||||
# for default values check:
|
||||
# https://github.com/python-semantic-release/python-semantic-release/blob/v7.32.2/semantic_release/defaults.cfg
|
||||
|
||||
version_source = "tag_only"
|
||||
branch = "main"
|
||||
|
||||
# configure types which should trigger minor and patch version bumps respectively
|
||||
# (note that they must be a subset of the configured allowed types):
|
||||
parser_angular_allowed_types = "build,chore,ci,docs,feat,fix,perf,style,refactor,test"
|
||||
parser_angular_minor_types = "feat"
|
||||
parser_angular_patch_types = "fix,perf"
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
# Network settings
|
||||
export PORT="${PORT:-5001}"
|
||||
export HOST="${HOST:-"0.0.0.0"}"
|
||||
|
||||
# Performance settings
|
||||
UVICORN_WORKERS="${UVICORN_WORKERS:-1}"
|
||||
|
||||
# Development settings
|
||||
export WITH_UI="${WITH_UI:-"true"}"
|
||||
export RELOAD=${RELOAD:-"false"}
|
||||
|
||||
# --------------------------------------
|
||||
# Process env settings
|
||||
|
||||
EXTRA_ARGS=""
|
||||
if [ "$RELOAD" == "true" ]; then
|
||||
EXTRA_ARGS="$EXTRA_ARGS --reload"
|
||||
fi
|
||||
|
||||
# Launch
|
||||
exec poetry run uvicorn \
|
||||
docling_serve.app:app \
|
||||
--host=${HOST} \
|
||||
--port=${PORT} \
|
||||
--timeout-keep-alive=600 \
|
||||
${EXTRA_ARGS} \
|
||||
--workers=${UVICORN_WORKERS}
|
||||
Reference in New Issue
Block a user