adudeja's picture
fix build issue
212bd1b
Raw History Blame Contribute Delete
4.7 kB
"""
DocuLens β€” Hugging Face Spaces entry point (Gradio SDK wrapper).
HF free-tier Spaces only support Gradio/Streamlit SDK, not Docker.
This file creates a minimal Gradio UI and mounts all FastAPI API routes
onto the same server, so the REST API works exactly as before.
How it works:
1. Build a FastAPI app with all DocuLens API routes.
2. Create a minimal Gradio Blocks UI.
3. Mount Gradio onto the FastAPI app via gr.mount_gradio_app().
4. Expose the combined ASGI app as `app` β€” HF Spaces detects and
serves it automatically. We do NOT call uvicorn.run() or
demo.launch() ourselves β€” HF handles that.
The `demo` variable is renamed to `_gradio_ui` so HF's runner doesn't
auto-launch it separately (which would cause a port conflict).
"""
import os
import logging
# ---------------------------------------------------------------------------
# Structured logging β€” configure once, before anything else logs
# ---------------------------------------------------------------------------
LOG_LEVEL = os.environ.get("LOG_LEVEL", "INFO").upper()
LOG_FORMAT = os.environ.get(
"LOG_FORMAT",
"%(asctime)s [%(levelname)s] %(name)s: %(message)s",
)
logging.basicConfig(
level=getattr(logging, LOG_LEVEL, logging.INFO),
format=LOG_FORMAT,
datefmt="%Y-%m-%d %H:%M:%S",
)
# Quieten noisy third-party loggers
for _noisy in ("httpx", "httpcore", "urllib3", "uvicorn.access"):
logging.getLogger(_noisy).setLevel(logging.WARNING)
logger = logging.getLogger(__name__)
# ---------------------------------------------------------------------------
# 1. Build the FastAPI app with all API routes (same as main.py)
# ---------------------------------------------------------------------------
from fastapi import FastAPI
from fastapi.responses import JSONResponse
fastapi_app = FastAPI(
title="DocuLens",
description="AI-Powered Invoice & Document Extraction API",
version="1.2.0",
)
# CORS
from api import add_cors_middleware
add_cors_middleware(fastapi_app)
# Mount all API routers
from api import api_router
from api_runs import runs_router
from api_prompts import prompts_router
from api_chat import chat_router
from api_webhooks import webhooks_router
from api_export import export_router
from api_batch import batch_router
from api_tiers import tiers_router
from api_golden import golden_router
fastapi_app.include_router(api_router)
fastapi_app.include_router(runs_router)
fastapi_app.include_router(prompts_router)
fastapi_app.include_router(chat_router)
fastapi_app.include_router(webhooks_router)
fastapi_app.include_router(export_router)
fastapi_app.include_router(batch_router)
fastapi_app.include_router(tiers_router)
fastapi_app.include_router(golden_router)
@fastapi_app.get("/health")
async def health():
return JSONResponse(
{
"service": "DocuLens",
"version": "1.2.0",
"status": "running",
"docs": "/docs",
}
)
# ---------------------------------------------------------------------------
# 2. Create a minimal Gradio UI (required by HF Spaces free-tier SDK)
# ---------------------------------------------------------------------------
import gradio as gr
# Use _gradio_ui (not `demo`) to prevent HF from auto-launching it
# separately β€” we mount it onto our FastAPI app below instead.
with gr.Blocks(title="DocuLens", theme=gr.themes.Soft()) as _gradio_ui:
gr.Markdown(
"""
# DocuLens API
AI-Powered Invoice & Document Extraction.
**API Documentation:** Navigate to [`/docs`](/docs) for the interactive
Swagger UI, or [`/redoc`](/redoc) for ReDoc.
**Health check:** [`/health`](/health)
"""
)
# ---------------------------------------------------------------------------
# 3. Mount Gradio onto the FastAPI app so both coexist on the same server.
# ---------------------------------------------------------------------------
app = gr.mount_gradio_app(fastapi_app, _gradio_ui, path="/")
# ---------------------------------------------------------------------------
# 4. Start the server β€” no __main__ guard.
#
# HF's `gradio` CLI may import rather than exec the file, so
# `if __name__ == "__main__"` can be skipped. We start uvicorn
# unconditionally on port 7860 (HF's health-check port).
# The Gradio Blocks is named `_gradio_ui` (not `demo`) to prevent
# HF from auto-launching a second server that would conflict.
# ---------------------------------------------------------------------------
import uvicorn
port = int(os.environ.get("PORT", "7860"))
logger.info("Starting DocuLens on port %d", port)
uvicorn.run(app, host="0.0.0.0", port=port)