From 68772bb6f0a87b71094a08ff851f5754c6ca6163 Mon Sep 17 00:00:00 2001 From: Michele Dolfi <97102151+dolfim-ibm@users.noreply.github.com> Date: Wed, 26 Mar 2025 18:54:54 -0400 Subject: [PATCH] feat: Offline static files (#109) Signed-off-by: Michele Dolfi --- docling_serve/app.py | 55 +++++++++++++++++++++++++++++++++++--- docling_serve/gradio_ui.py | 22 ++++++++++++--- docling_serve/settings.py | 1 + docs/configuration.md | 1 + 4 files changed, 72 insertions(+), 7 deletions(-) diff --git a/docling_serve/app.py b/docling_serve/app.py index 5ef0abf..249ce81 100644 --- a/docling_serve/app.py +++ b/docling_serve/app.py @@ -18,7 +18,13 @@ from fastapi import ( WebSocketDisconnect, ) from fastapi.middleware.cors import CORSMiddleware +from fastapi.openapi.docs import ( + get_redoc_html, + get_swagger_ui_html, + get_swagger_ui_oauth2_redirect_html, +) from fastapi.responses import RedirectResponse +from fastapi.staticfiles import StaticFiles from docling.datamodel.base_models import DocumentStream @@ -116,8 +122,18 @@ def create_app(): # noqa: C901 version = "0.0.0" + offline_docs_assets = False + if ( + docling_serve_settings.static_path is not None + and (docling_serve_settings.static_path).is_dir() + ): + offline_docs_assets = True + _log.info("Found static assets.") + app = FastAPI( title="Docling Serve", + docs_url=None if offline_docs_assets else "/docs", + redoc_url=None if offline_docs_assets else "/redocs", lifespan=lifespan, version=version, ) @@ -157,6 +173,38 @@ def create_app(): # noqa: C901 "or `pip install gradio`" ) + ############################# + # Offline assets definition # + ############################# + if offline_docs_assets: + app.mount( + "/static", + StaticFiles(directory=docling_serve_settings.static_path), + name="static", + ) + + @app.get("/docs", include_in_schema=False) + async def custom_swagger_ui_html(): + return get_swagger_ui_html( + openapi_url=app.openapi_url, + title=app.title + " - Swagger UI", + oauth2_redirect_url=app.swagger_ui_oauth2_redirect_url, + swagger_js_url="/static/swagger-ui-bundle.js", + swagger_css_url="/static/swagger-ui.css", + ) + + @app.get(app.swagger_ui_oauth2_redirect_url, include_in_schema=False) + async def swagger_ui_redirect(): + return get_swagger_ui_oauth2_redirect_html() + + @app.get("/redoc", include_in_schema=False) + async def redoc_html(): + return get_redoc_html( + openapi_url=app.openapi_url, + title=app.title + " - ReDoc", + redoc_js_url="/static/redoc.standalone.js", + ) + ############################# # API Endpoints definitions # ############################# @@ -164,9 +212,10 @@ def create_app(): # noqa: C901 # Favicon @app.get("/favicon.ico", include_in_schema=False) async def favicon(): - response = RedirectResponse( - url="https://raw.githubusercontent.com/docling-project/docling/refs/heads/main/docs/assets/logo.svg" - ) + logo_url = "https://raw.githubusercontent.com/docling-project/docling/refs/heads/main/docs/assets/logo.svg" + if offline_docs_assets: + logo_url = "/static/logo.svg" + response = RedirectResponse(url=logo_url) return response @app.get("/health") diff --git a/docling_serve/gradio_ui.py b/docling_serve/gradio_ui.py index c96b763..6d222c1 100644 --- a/docling_serve/gradio_ui.py +++ b/docling_serve/gradio_ui.py @@ -8,15 +8,29 @@ import gradio as gr import requests from docling_serve.helper_functions import _to_list_of_strings -from docling_serve.settings import uvicorn_settings +from docling_serve.settings import docling_serve_settings, uvicorn_settings logger = logging.getLogger(__name__) +############################ +# Path of static artifacts # +############################ + +logo_path = "https://raw.githubusercontent.com/docling-project/docling/refs/heads/main/docs/assets/logo.svg" +js_components_url = "https://unpkg.com/@docling/docling-components@0.0.3" +if ( + docling_serve_settings.static_path is not None + and docling_serve_settings.static_path.is_dir() +): + logo_path = str(docling_serve_settings.static_path / "logo.svg") + js_components_url = "/static/docling-components.js" + + ############################## # Head JS for web components # ############################## -head = """ - +head = f""" + """ ################# @@ -360,7 +374,7 @@ with gr.Blocks( with gr.Column(scale=1, min_width=90): try: gr.Image( - "https://raw.githubusercontent.com/docling-project/docling/refs/heads/main/docs/assets/logo.svg", + logo_path, height=80, width=80, show_download_button=False, diff --git a/docling_serve/settings.py b/docling_serve/settings.py index 90676b3..9a48a04 100644 --- a/docling_serve/settings.py +++ b/docling_serve/settings.py @@ -30,6 +30,7 @@ class DoclingServeSettings(BaseSettings): enable_ui: bool = False artifacts_path: Optional[Path] = None + static_path: Optional[Path] = None options_cache_size: int = 2 allow_external_plugins: bool = False diff --git a/docs/configuration.md b/docs/configuration.md index e375794..769fd3d 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -33,6 +33,7 @@ THe following table describes the options to configure the Docling Serve app. | CLI option | ENV | Default | Description | | -----------|-----|---------|-------------| | `--artifacts-path` | `DOCLING_SERVE_ARTIFACTS_PATH` | unset | If set to a valid directory, the model weights will be loaded from this path | +| | `DOCLING_SERVE_STATIC_PATH` | unset | If set to a valid directory, the static assets for the docs and ui will be loaded from this path | | `--enable-ui` | `DOCLING_SERVE_ENABLE_UI` | `false` | Enable the demonstrator UI. | | | `DOCLING_SERVE_OPTIONS_CACHE_SIZE` | `2` | How many DocumentConveter objects (including their loaded models) to keep in the cache. | | | `DOCLING_SERVE_CORS_ORIGINS` | `["*"]` | A list of origins that should be permitted to make cross-origin requests. |