FastAPI service wrapping the Surya-OCR-2 model (datalab-to) served through vLLM: legacy /v1/api/ai/* endpoints, an OpenAI-compatible /v1/chat/completions endpoint, a coalescing request batcher, a local OCR CLI, Docker packaging, multilingual example outputs, and quantization/concurrency benchmarks. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
98 lines
2.8 KiB
Python
98 lines
2.8 KiB
Python
import json
|
|
import logging
|
|
import logging.config
|
|
import time
|
|
import uuid
|
|
|
|
import yaml
|
|
from fastapi import FastAPI, Request
|
|
from fastapi.exceptions import RequestValidationError
|
|
from fastapi.responses import JSONResponse
|
|
|
|
from surya.endpoint.legacy import router as legacy_router
|
|
from surya.endpoint.openai import router as openai_router
|
|
from surya.endpoint.service import _error_response
|
|
|
|
with open("logger.yaml", "r", encoding="utf-8") as f:
|
|
config = yaml.safe_load(f.read())
|
|
logging.config.dictConfig(config)
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
app = FastAPI(
|
|
title="OCR SERVICE API SERVICE",
|
|
version="1.0",
|
|
docs_url="/v1/api/ai/swagger",
|
|
openapi_url="/v1/api/ai/openapi.json",
|
|
)
|
|
|
|
|
|
@app.exception_handler(RequestValidationError)
|
|
async def validation_exception_handler(request: Request, exc: RequestValidationError):
|
|
request_id = getattr(request.state, "request_id", "-")
|
|
logger.warning(
|
|
"request_validation_failed request_id=%s method=%s path=%s errors=%s",
|
|
request_id,
|
|
request.method,
|
|
request.url.path,
|
|
exc.errors(),
|
|
)
|
|
return JSONResponse(
|
|
status_code=200,
|
|
content={
|
|
"data": [],
|
|
"message": json.dumps({"error info": exc.errors()}, default=str),
|
|
"code": 422,
|
|
},
|
|
)
|
|
|
|
|
|
@app.middleware("http")
|
|
async def allow_openapi_unauthorized(request: Request, call_next):
|
|
if request.url.path == "/v1/api/ai/openapi.json":
|
|
response = await call_next(request)
|
|
return response
|
|
return await call_next(request)
|
|
|
|
|
|
@app.middleware("http")
|
|
async def log_request_response(request, call_next):
|
|
request_id = uuid.uuid4().hex
|
|
request.state.request_id = request_id
|
|
start = time.perf_counter()
|
|
client = request.client.host if request.client else "-"
|
|
logger.info(
|
|
"request_start request_id=%s method=%s path=%s client=%s",
|
|
request_id,
|
|
request.method,
|
|
request.url.path,
|
|
client,
|
|
)
|
|
try:
|
|
response = await call_next(request)
|
|
except Exception as e:
|
|
duration_ms = (time.perf_counter() - start) * 1000
|
|
logger.exception(
|
|
"request_unhandled_exception request_id=%s method=%s path=%s duration_ms=%.2f",
|
|
request_id,
|
|
request.method,
|
|
request.url.path,
|
|
duration_ms,
|
|
exc_info=True,
|
|
)
|
|
return JSONResponse(status_code=500, content=_error_response(e))
|
|
duration_ms = (time.perf_counter() - start) * 1000
|
|
logger.info(
|
|
"request_end request_id=%s method=%s path=%s status_code=%s duration_ms=%.2f",
|
|
request_id,
|
|
request.method,
|
|
request.url.path,
|
|
response.status_code,
|
|
duration_ms,
|
|
)
|
|
return response
|
|
|
|
|
|
app.include_router(legacy_router)
|
|
app.include_router(openai_router)
|