71 lines
2.6 KiB
Python
71 lines
2.6 KiB
Python
"""仅挂载 LLM 路由的 FastAPI 应用,避免在轻量测试环境中导入完整 app(含 Celery/film 等)。"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from contextlib import asynccontextmanager
|
|
|
|
from fastapi import FastAPI, HTTPException, Request
|
|
from fastapi.exceptions import RequestValidationError
|
|
from fastapi.responses import JSONResponse
|
|
|
|
from app.api.v1.routes import llm
|
|
from app.bootstrap import bootstrap_all_registries
|
|
from app.config import settings
|
|
from app.schemas.common import ApiResponse
|
|
|
|
|
|
def _error_message(detail: object) -> str:
|
|
if isinstance(detail, str):
|
|
return detail
|
|
if isinstance(detail, list):
|
|
parts = []
|
|
for item in detail:
|
|
if isinstance(item, dict) and "msg" in item:
|
|
loc = item.get("loc", ())
|
|
loc_str = ".".join(str(x) for x in loc if x != "body")
|
|
parts.append(f"{loc_str}: {item['msg']}" if loc_str else item["msg"])
|
|
else:
|
|
parts.append(str(item))
|
|
return "; ".join(parts) if parts else "Validation error"
|
|
return str(detail)
|
|
|
|
|
|
async def http_exception_handler(request: Request, exc: Exception) -> JSONResponse:
|
|
if isinstance(exc, HTTPException):
|
|
code = exc.status_code
|
|
message = _error_message(exc.detail)
|
|
else:
|
|
code = 500
|
|
message = "Internal server error"
|
|
body = ApiResponse[None](code=code, message=message, data=None, meta=None).model_dump()
|
|
return JSONResponse(status_code=code, content=body)
|
|
|
|
|
|
async def validation_exception_handler(request: Request, exc: Exception) -> JSONResponse:
|
|
assert isinstance(exc, RequestValidationError)
|
|
message = _error_message(exc.errors())
|
|
body = ApiResponse[None](code=422, message=message, data=None, meta=None).model_dump()
|
|
return JSONResponse(status_code=422, content=body)
|
|
|
|
|
|
@asynccontextmanager
|
|
async def _lifespan(app: FastAPI):
|
|
bootstrap_all_registries()
|
|
yield
|
|
|
|
|
|
def build_llm_only_app() -> FastAPI:
|
|
"""与主应用相同的错误壳与 /api/v1/llm 前缀,仅不含 film/studio 等路由。"""
|
|
application = FastAPI(
|
|
title=settings.app_name,
|
|
version="0.1.0",
|
|
lifespan=_lifespan,
|
|
docs_url=None,
|
|
redoc_url=None,
|
|
)
|
|
application.add_exception_handler(RequestValidationError, validation_exception_handler)
|
|
application.add_exception_handler(HTTPException, http_exception_handler)
|
|
application.add_exception_handler(Exception, http_exception_handler)
|
|
application.include_router(llm.router, prefix=f"{settings.api_v1_prefix}/llm", tags=["llm"])
|
|
return application
|