feat: Make docling core optional#1003
Conversation
Move docling-related packages into an optional 'docling' dependency group and add zxcvbn. Add runtime checks (is_docling_available) and a DoclingNotInstalledError to detect missing openrag[docling]. Make the document converter lazy-initialized in AppClients.converter and guard usages in processors and DocumentService to raise a clear error when docling isn't present. Add HTTP handlers and upload-specific returns that map missing-docling errors to 503 responses and skip warm-up when docling is not installed. Also update warm_up_docling to exit early if docling is absent.
Revamp Dockerfile.backend to a multi-stage build with an INSTALL_DOCLING build-arg: the builder stage installs dependencies and (optionally) downloads docling/EasyOCR models when INSTALL_DOCLING=1, while the final stage is a minimal runtime that copies the installed app. pyproject.toml now moves torch to an optional 'torch' extra and ensures docling-serve is managed via extras so heavy ML runtimes are not required by default. src/main.py defers and guards the torch import, logging CUDA info only when torch is present. src/tui/managers/docling_manager.py adds a runtime check for docling-serve and fails gracefully with guidance if it's not installed. These changes allow smaller images by default and make startup resilient when optional ML packages are absent.
Convert Dockerfile.backend to a multi-stage BuildKit-friendly layout (syntax header + base/builder/runtime stages). Install uv in a lightweight base image, move build-time deps to a builder stage, and make docling/model installation optional via ARG INSTALL_DOCLING=1. Use --no-install-recommends for apt, add cache mount for uv, conditionally run uv sync --extra docling and download docling/EasyOCR models into /app/.EasyOCR when requested, and clean temporary caches. The final runtime stage remains minimal and copies the prepared /app from the builder.
…cling-core-optional
|
once #1002 merges this PR would point to Main branch |
There was a problem hiding this comment.
Pull request overview
This pull request makes docling and torch optional dependencies to significantly reduce the backend Docker image size from ~4GB to ~500MB. The changes introduce a pattern of lazy initialization and availability checks for heavy dependencies, allowing the application to run without these packages installed while providing clear error messages when optional features are attempted without the required extras.
Changes:
- Moved docling, docling-core, easyocr, and torch from core dependencies to optional extras in pyproject.toml
- Added availability checks and custom exception handling for missing optional dependencies
- Implemented lazy initialization for the document converter to defer imports until actual use
- Refactored Dockerfile to use multi-stage builds with conditional installation of optional dependencies
Reviewed changes
Copilot reviewed 10 out of 11 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| pyproject.toml | Moved docling and torch dependencies to optional extras section |
| uv.lock | Updated dependency markers to reflect optional status with extra == conditions |
| warm_up_docling.py | Added availability check to skip warm-up when docling is not installed |
| src/utils/document_processing.py | Added is_docling_available() function, DoclingNotInstalledError exception class, and proper import guards |
| src/tui/managers/docling_manager.py | Added availability check for docling_serve package |
| src/services/document_service.py | Added availability check before importing docling_core |
| src/models/processors.py | Added availability check before using docling converter |
| src/main.py | Made torch import optional and added global exception handler for DoclingNotInstalledError |
| src/config/settings.py | Changed converter to lazy-initialized property to defer docling imports |
| src/api/upload.py | Added local exception handling for DoclingNotInstalledError (redundant with global handler) |
| Dockerfile.backend | Refactored to multi-stage build with conditional docling installation via build arg |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot <[email protected]>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 11 changed files in this pull request and generated no new comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Introduce require_docling() in utils.document_processing to raise DoclingNotInstalledError when the docling extra is missing, and replace scattered is_docling_available() checks with require_docling() in processors and services. Update create_document_converter and get_worker_converter to call require_docling(). Remove the special-case DoclingNotInstalledError handling from the upload endpoint so docling-related errors are raised consistently. Aims to centralize docling availability logic and simplify error handling across the codebase.
…penrag into make-docling-core-optional
Capture and log docling start output and architecture in Makefile; extract endpoint from output, default to http://localhost:5001 if missing, run status checks, and emit a clear error if health checks fail. Refactor docling host detection to try well-known hostnames first, use container-specific fallbacks (gateway/bridge) and add clearer logging. Treat .md files like .txt for simple processing to skip docling in processors and document service. Also add several local documentation PDFs to .gitignore.
Introduce a configurable startup timeout for docling and make CI fail-fast with cleanup. Added a --timeout argument to scripts/docling_ctl.py and forwarded it to DoclingManager.start; DoclingManager.start now accepts a timeout and waits up to that many seconds for the service to listen (with periodic log updates). Updated Makefile test-ci and test-ci-local to pass --timeout 180, and on startup failure the pipeline now stops docling, brings down compose services, and exits with error. Also clarified some startup/failure log messages.
Simplify how the external docling serve process is started by removing shutil-based checks and fallbacks for `uv` or `python -m docling_serve`. The code now always builds a fixed `uvx ... docling-serve==1.5.0 run` command (keeping platform-specific OCR package selection). Also small whitespace added in the docstring. This reduces branching and assumes `uvx` is available in the runtime environment.
Remove the apt-get install libgl1 step from the integration workflow and add --with opencv-python-headless to the uvx install args for docling-serve==1.5.0. This switches OpenCV to a headless Python package to avoid needing system GL libraries on the CI runner.
On non-mac platforms, create a temporary uvx override file to replace opencv-python with opencv-python-headless so headless CI runners lacking libGL can run docling. Add tempfile import, build the uvx command to pass --override and --with opencv-python-headless, and ensure the override file is removed after resolution or on errors. Minor command assembly refactor and cleanup on FileNotFoundError/exception paths.
|
@phact Mike have fixed the Tests and will merge it soon. |
Reduced Backend Image size from ~4GB to ~500MB