Spaces:
Sleeping
Sleeping
| """ | |
| Modal deployment configuration for PDF Layout Extractor Flask app. | |
| Deploy with: modal deploy modal_app.py | |
| """ | |
| import modal | |
| # Create a Modal image with GPU support and all dependencies | |
| image = ( | |
| modal.Image.debian_slim(python_version="3.12") | |
| .apt_install( | |
| "build-essential", | |
| "gcc", | |
| "g++", | |
| "libgl1", | |
| "libglib2.0-0", # Required for cv2 (provides libgthread-2.0.so.0) | |
| "libsm6", # Required for cv2 | |
| "libxext6", # Required for cv2 | |
| "libxrender-dev", # Required for cv2 | |
| "libgomp1", # Required for cv2 | |
| "libffi-dev", | |
| "libjpeg62-turbo-dev", | |
| "zlib1g-dev", | |
| "netcat-openbsd", | |
| ) | |
| .pip_install( | |
| "torch>=2.0.0", | |
| "torchvision>=0.15.0", | |
| "doclayout-yolo>=0.0.4", | |
| "huggingface-hub>=1.1.2", | |
| "loguru>=0.7.3", | |
| "pillow>=12.0.0", | |
| "pymupdf>=1.26.6", | |
| "pymupdf-layout>=0.0.15", | |
| "pypdfium2>=5.0.0", | |
| "pymupdf4llm>=0.1.9", | |
| "flask>=3.0.0", | |
| "fastapi>=0.109.0", # Required for Modal web endpoints | |
| "werkzeug>=3.0.0", | |
| "gunicorn>=21.2.0", | |
| "asgiref>=3.7.0", # For WSGI-to-ASGI conversion | |
| ) | |
| .run_commands( | |
| "mkdir -p /app/uploads /app/output /app/static /app/templates" | |
| ) | |
| # Copy application files directly into the image | |
| .add_local_dir("static", remote_path="/app/static") | |
| .add_local_dir("templates", remote_path="/app/templates") | |
| .add_local_file("app.py", remote_path="/app/app.py") | |
| .add_local_file("main.py", remote_path="/app/main.py") | |
| ) | |
| # Create the Modal app | |
| app = modal.App("pdf-layout-extractor", image=image) | |
| # GPU configuration - using T4 for cheapest option (~$0.50/hour while active) | |
| # For no GPU (CPU only), set gpu=None (much cheaper but slower) | |
| # Valid options: "T4", "A10G", "A100", or None | |
| def flask_app(): | |
| """ | |
| Expose the Flask app as an ASGI application for Modal. | |
| Flask is WSGI, so we convert it to ASGI using a wrapper. | |
| """ | |
| import sys | |
| import os | |
| from pathlib import Path | |
| # Set working directory | |
| os.chdir("/app") | |
| sys.path.insert(0, "/app") | |
| # Import Flask app | |
| from app import app as flask_app_instance | |
| # Convert Flask WSGI app to ASGI for Modal | |
| # Using asgiref's WSGI-to-ASGI adapter | |
| from asgiref.wsgi import WsgiToAsgi | |
| asgi_app = WsgiToAsgi(flask_app_instance) | |
| return asgi_app | |
| # Alternative: Deploy as a web endpoint with automatic HTTPS | |
| def health(): | |
| """Health check endpoint.""" | |
| return {"status": "ok", "service": "pdf-layout-extractor"} | |
| if __name__ == "__main__": | |
| # For local testing with Modal dev server: | |
| # Run: modal serve modal_app.py | |
| pass | |