"""FastAPI 应用入口""" import asyncio from contextlib import asynccontextmanager from fastapi import Depends, FastAPI from loguru import logger from app.api.agent import router from app.api.conversations import router as conversation_router from app.api.materials import router as material_router from app.core.auth import get_current_user from app.core.exceptions import register_exception_handlers from app.core.logging import setup_logging @asynccontextmanager async def lifespan(application: FastAPI): setup_logging() from app.clients.decision import close_decision_engine from app.clients.material_mcp import ( MaterialFieldServiceError, start_material_server, stop_material_server, ) from app.clients.user_center import close_user_center from app.observability.tracing import flush_tracing from app.processor.graph import build_graph from app.services.agent import AgentService from app.services.material_mongo import warm_material_cache from app.services.persistence import open_persistence # 按配置选择 MongoDB 或进程内存,会话记录与图检查点使用同一种存储 persistence = await open_persistence() application.state.conversation_store = persistence.store material_server = None try: # 物料目录启动时载入内存,agent 规划与前端接口共用,避免重复查询数据库 await warm_material_cache(persistence.db) # 物料字段服务给模型查询字段;启动失败只记录,需要查字段的修改执行时会报错 try: material_server = await start_material_server() except MaterialFieldServiceError as error: logger.error("{},修改组件属性和新增组件将无法执行", error) application.state.agent_service = AgentService( build_graph(persistence.checkpointer), persistence.store ) yield finally: # 共享客户端只在应用退出时关闭,追踪刷新放到线程避免阻塞事件循环 try: await stop_material_server(material_server) finally: try: await persistence.close() finally: try: await close_decision_engine() finally: try: await close_user_center() finally: try: await asyncio.to_thread(flush_tracing) finally: await logger.complete() def create_app() -> FastAPI: application = FastAPI(title="Screen Agent API", lifespan=lifespan) # 统一处理请求校验、HTTP 错误和未捕获异常 register_exception_handlers(application) # 统一 /api 前缀,方便前端代理和反向代理只配置一条规则 # 会话和 agent 接口需登录,物料目录是全局公开数据 authenticated = [Depends(get_current_user)] application.include_router(router, prefix="/api", dependencies=authenticated) application.include_router( conversation_router, prefix="/api", dependencies=authenticated ) application.include_router(material_router, prefix="/api") return application app = create_app()