From 873d05aefe141c63b9c1cf53b23b4fa8c96de05d Mon Sep 17 00:00:00 2001 From: Michele Dolfi <97102151+dolfim-ibm@users.noreply.github.com> Date: Tue, 17 Jun 2025 09:54:00 -0500 Subject: [PATCH] feat: use redocs and scalar as api docs (#228) Signed-off-by: Michele Dolfi --- docling_serve/__main__.py | 2 ++ docling_serve/app.py | 18 ++++++++++++++---- docling_serve/datamodel/convert.py | 8 ++++++-- docling_serve/helper_functions.py | 2 +- pyproject.toml | 2 ++ uv.lock | 11 +++++++++++ 6 files changed, 36 insertions(+), 7 deletions(-) diff --git a/docling_serve/__main__.py b/docling_serve/__main__.py index 003c8db..c22d337 100644 --- a/docling_serve/__main__.py +++ b/docling_serve/__main__.py @@ -113,11 +113,13 @@ def _run( protocol = "https" if run_ssl else "http" url = f"{protocol}://{uvicorn_settings.host}:{uvicorn_settings.port}" url_docs = f"{url}/docs" + url_scalar = f"{url}/scalar" 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}[/]") + console.print(f"Scalar docs at [link={url_docs}]{url_scalar}[/]") if docling_serve_settings.enable_ui: console.print(f"UI at [link={url_ui}]{url_ui}[/]") diff --git a/docling_serve/app.py b/docling_serve/app.py index e18e353..ff8b1a5 100644 --- a/docling_serve/app.py +++ b/docling_serve/app.py @@ -25,6 +25,7 @@ from fastapi.openapi.docs import ( ) from fastapi.responses import RedirectResponse from fastapi.staticfiles import StaticFiles +from scalar_fastapi import get_scalar_api_reference from docling.datamodel.base_models import DocumentStream @@ -140,8 +141,8 @@ def create_app(): # noqa: C901 app = FastAPI( title="Docling Serve", - docs_url=None if offline_docs_assets else "/docs", - redoc_url=None if offline_docs_assets else "/redocs", + docs_url=None if offline_docs_assets else "/swagger", + redoc_url=None if offline_docs_assets else "/docs", lifespan=lifespan, version=version, ) @@ -192,7 +193,7 @@ def create_app(): # noqa: C901 name="static", ) - @app.get("/docs", include_in_schema=False) + @app.get("/swagger", include_in_schema=False) async def custom_swagger_ui_html(): return get_swagger_ui_html( openapi_url=app.openapi_url, @@ -206,7 +207,7 @@ def create_app(): # noqa: C901 async def swagger_ui_redirect(): return get_swagger_ui_oauth2_redirect_html() - @app.get("/redoc", include_in_schema=False) + @app.get("/docs", include_in_schema=False) async def redoc_html(): return get_redoc_html( openapi_url=app.openapi_url, @@ -214,6 +215,15 @@ def create_app(): # noqa: C901 redoc_js_url="/static/redoc.standalone.js", ) + @app.get("/scalar", include_in_schema=False) + async def scalar_html(): + return get_scalar_api_reference( + openapi_url=app.openapi_url, + title=app.title, + scalar_favicon_url="https://raw.githubusercontent.com/docling-project/docling/refs/heads/main/docs/assets/logo.svg", + # hide_client_button=True, # not yet released but in main + ) + ######################## # Async / Sync helpers # ######################## diff --git a/docling_serve/datamodel/convert.py b/docling_serve/datamodel/convert.py index f48438a..b94fe73 100644 --- a/docling_serve/datamodel/convert.py +++ b/docling_serve/datamodel/convert.py @@ -132,7 +132,11 @@ class ConvertDocumentsOptions(BaseModel): f"Allowed values: {', '.join([v.value for v in OutputFormat])}. " "Optional, defaults to Markdown." ), - examples=[[OutputFormat.MARKDOWN]], + examples=[ + [OutputFormat.MARKDOWN], + [OutputFormat.MARKDOWN, OutputFormat.JSON], + [v.value for v in OutputFormat], + ], ), ] = [OutputFormat.MARKDOWN] @@ -231,7 +235,7 @@ class ConvertDocumentsOptions(BaseModel): PageRange, Field( description="Only convert a range of pages. The page number starts at 1.", - examples=[(1, 4)], + examples=[DEFAULT_PAGE_RANGE, (1, 4)], ), ] = DEFAULT_PAGE_RANGE diff --git a/docling_serve/helper_functions.py b/docling_serve/helper_functions.py index 20982d1..2128ed7 100644 --- a/docling_serve/helper_functions.py +++ b/docling_serve/helper_functions.py @@ -36,7 +36,7 @@ def FormDepends(cls: type[BaseModel]): annotation = model_field.annotation description = model_field.description default = ( - Form(..., description=description) + Form(..., description=description, examples=model_field.examples) if model_field.is_required() else Form( model_field.default, diff --git a/pyproject.toml b/pyproject.toml index fe9c4a6..f4e0059 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,6 +42,7 @@ dependencies = [ "typer~=0.12", "uvicorn[standard]>=0.29.0,<1.0.0", "websockets~=14.0", + "scalar-fastapi>=1.0.3", ] [project.optional-dependencies] @@ -213,6 +214,7 @@ module = [ "kfp.*", "kfp_server_api.*", "mlx_vlm.*", + "scalar_fastapi.*", ] ignore_missing_imports = true diff --git a/uv.lock b/uv.lock index ddc6275..483f999 100644 --- a/uv.lock +++ b/uv.lock @@ -722,6 +722,7 @@ dependencies = [ { name = "pydantic", marker = "platform_machine != 'x86_64' or sys_platform != 'darwin' or (extra == 'extra-13-docling-serve-cpu' and extra == 'extra-13-docling-serve-cu124') or (extra == 'extra-13-docling-serve-cpu' and extra == 'extra-13-docling-serve-flash-attn')" }, { name = "pydantic-settings", marker = "platform_machine != 'x86_64' or sys_platform != 'darwin' or (extra == 'extra-13-docling-serve-cpu' and extra == 'extra-13-docling-serve-cu124') or (extra == 'extra-13-docling-serve-cpu' and extra == 'extra-13-docling-serve-flash-attn')" }, { name = "python-multipart", marker = "platform_machine != 'x86_64' or sys_platform != 'darwin' or (extra == 'extra-13-docling-serve-cpu' and extra == 'extra-13-docling-serve-cu124') or (extra == 'extra-13-docling-serve-cpu' and extra == 'extra-13-docling-serve-flash-attn')" }, + { name = "scalar-fastapi", marker = "platform_machine != 'x86_64' or sys_platform != 'darwin' or (extra == 'extra-13-docling-serve-cpu' and extra == 'extra-13-docling-serve-cu124') or (extra == 'extra-13-docling-serve-cpu' and extra == 'extra-13-docling-serve-flash-attn')" }, { name = "typer", marker = "platform_machine != 'x86_64' or sys_platform != 'darwin' or (extra == 'extra-13-docling-serve-cpu' and extra == 'extra-13-docling-serve-cu124') or (extra == 'extra-13-docling-serve-cpu' and extra == 'extra-13-docling-serve-flash-attn')" }, { name = "uvicorn", extra = ["standard"], marker = "platform_machine != 'x86_64' or sys_platform != 'darwin' or (extra == 'extra-13-docling-serve-cpu' and extra == 'extra-13-docling-serve-cu124') or (extra == 'extra-13-docling-serve-cpu' and extra == 'extra-13-docling-serve-flash-attn')" }, { name = "websockets", marker = "platform_machine != 'x86_64' or sys_platform != 'darwin' or (extra == 'extra-13-docling-serve-cpu' and extra == 'extra-13-docling-serve-cu124') or (extra == 'extra-13-docling-serve-cpu' and extra == 'extra-13-docling-serve-flash-attn')" }, @@ -781,6 +782,7 @@ requires-dist = [ { name = "pydantic-settings", specifier = "~=2.4" }, { name = "python-multipart", specifier = ">=0.0.14,<0.1.0" }, { name = "rapidocr-onnxruntime", marker = "python_full_version < '3.13' and extra == 'rapidocr'", specifier = "~=1.4" }, + { name = "scalar-fastapi", specifier = ">=1.0.3" }, { name = "tesserocr", marker = "extra == 'tesserocr'", specifier = "~=2.7" }, { name = "torch", marker = "extra == 'cpu'", specifier = ">=2.6.0", index = "https://download.pytorch.org/whl/cpu", conflict = { package = "docling-serve", extra = "cpu" } }, { name = "torch", marker = "extra == 'cu124'", specifier = ">=2.6.0", index = "https://download.pytorch.org/whl/cu124", conflict = { package = "docling-serve", extra = "cu124" } }, @@ -4209,6 +4211,15 @@ torch = [ { name = "torch", version = "2.7.1+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(sys_platform != 'darwin' and extra == 'extra-13-docling-serve-cpu') or (extra == 'extra-13-docling-serve-cpu' and extra == 'extra-13-docling-serve-cu124') or (extra == 'extra-13-docling-serve-cpu' and extra == 'extra-13-docling-serve-flash-attn')" }, ] +[[package]] +name = "scalar-fastapi" +version = "1.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2c/04/08ae9e86f24cd9b43bc5b2068fc5d1dcd3e7efa6db860290734d7d742512/scalar_fastapi-1.0.3.tar.gz", hash = "sha256:9e9cb8398e298cd435a0171eebe1675b8899eb21e47c238db0d48783143f0ffb", size = 4107, upload-time = "2024-08-19T02:58:06.739Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/9e/ca0ffc3fc4788c6e1367b96a70f2d6ee33f91d580664a8142ba7ee292ef2/scalar_fastapi-1.0.3-py3-none-any.whl", hash = "sha256:4a47a140795097ad034518ce0e32940f2c54f0f4bc60e4c3289ca30a7e6f954d", size = 4579, upload-time = "2024-08-19T02:58:05.203Z" }, +] + [[package]] name = "scikit-image" version = "0.25.2"