"""FastAPI application factory for the RAGAS evaluation console. The app mounts three JSON API routers and serves the single-page static frontend. It imports rag_eval only lazily (inside the task manager worker), so the server starts even when the evaluation dependencies are not yet installed. """ from __future__ import annotations import logging from pathlib import Path from fastapi import FastAPI, Request from fastapi.encoders import jsonable_encoder from fastapi.exceptions import RequestValidationError from fastapi.responses import FileResponse, JSONResponse from fastapi.staticfiles import StaticFiles from webapp.api import evaluations, llm_profiles, pipeline, runs, scenarios, score STATIC_DIR = Path(__file__).resolve().parent / "static" logger = logging.getLogger("webapp.server") # OpenAPI tag metadata — controls the grouping and descriptions in /docs. OPENAPI_TAGS = [ { "name": "pipeline", "description": ( "**全链路评估 Pipeline API**\n\n" "一次调用完成「解析文档 → 生成题库 → RAGAS 评估 → 输出报告」全流程。\n\n" "**使用流程**\n" "1. `POST /api/pipeline/jobs` 提交任务,立即拿到 `job_id`。\n" "2. `GET /api/pipeline/jobs/{job_id}` 轮询 `status` / `phase` / `logs`。\n" "3. 当 `status=completed` 时,`result` 字段包含所有产物路径。\n\n" "**Pipeline 阶段**\n" "| phase | 说明 |\n" "|-------|------|\n" "| `parsing_documents` | 调用阿里云 DocMind 解析每份 PDF |\n" "| `generating_questions` | LLM 从文档片段生成草稿题库 |\n" "| `evaluating` | RAGAS 在线评测打分 |\n" "| `done` | 所有产物写入磁盘,任务完成 |" ), }, { "name": "evaluations", "description": ( "**单场景评估 API**\n\n" "基于已有 YAML 场景文件触发评估任务,并查询任务状态与日志。" ), }, { "name": "llm-profiles", "description": ( "**LLM 配置管理 API**\n\n" "增删改查已保存的 LLM 连接配置(模型名称、Base URL、API Key);" "支持连通性测试;可将配置一键写入场景 YAML 文件。" ), }, { "name": "runs", "description": "**评估运行列表 API**\n\n查询历史评估运行记录及详细报告数据。", }, { "name": "scenarios", "description": "**场景文件 API**\n\n扫描并列出 `scenarios/` 目录下所有可用的 YAML 场景文件。", }, { "name": "score", "description": ( "**实时评分 API(Dify 外部 Tool)**\n\n" "接受单条问答记录 `(question, answer, contexts, ground_truth)`,\n" "同步运行 RAGAS 指标打分,返回各指标得分和加权综合得分。\n\n" "适用场景:Dify Agent 在回答后即时调用,用于质量监控或自我改进。\n\n" "**鉴权**:若 `.env` 中配置了 `SCORE_API_TOKEN`,需携带 " "`Authorization: Bearer ` 请求头。" ), }, { "name": "meta", "description": "**系统 API**\n\n健康检查等基础接口。", }, ] def create_app() -> FastAPI: """Build and configure the FastAPI application instance.""" app = FastAPI( title="RAGAS 评估系统", description=( "西门子医疗影像 RAG 评估平台 API 文档。\n\n" "提供以下能力:\n" "- **Pipeline API** — 一键完成「解析文档 → 生成题库 → RAGAS 评估」全链路\n" "- **实时评分 API** — 供 Dify 外部 Tool 调用的单题 RAGAS 评分接口\n" "- **评估 API** — 基于 YAML 场景文件触发单次评估\n" "- **LLM 配置 API** — 管理多个 LLM 连接配置,支持连通性测试\n" "- **报告 API** — 查询历史运行记录与评估报告\n\n" "> **快速开始**:调用 `POST /api/pipeline/jobs` 传入 PDF 文件夹路径即可启动完整评估流程。" ), version="0.3.0", openapi_tags=OPENAPI_TAGS, ) app.include_router(runs.router) app.include_router(scenarios.router) app.include_router(evaluations.router) app.include_router(llm_profiles.router) app.include_router(pipeline.router) app.include_router(score.router) @app.exception_handler(RequestValidationError) async def validation_exception_handler(request: Request, exc: RequestValidationError) -> JSONResponse: """Log full validation error detail to help diagnose 422 responses.""" errors = jsonable_encoder(exc.errors()) logger.warning( "[422] validation error url=%s content_type=%s errors=%s", request.url.path, request.headers.get("content-type", ""), errors, ) return JSONResponse( status_code=422, content={"detail": errors}, ) @app.get("/api/health", tags=["meta"]) def health() -> dict[str, str]: """Report basic liveness so the UI can confirm the server is reachable.""" return {"status": "ok"} @app.get("/", include_in_schema=False) def index() -> FileResponse: """Serve the single-page console entry document.""" return FileResponse(STATIC_DIR / "index.html") # Serve CSS/JS assets under /static while keeping API routes at /api. app.mount("/static", StaticFiles(directory=STATIC_DIR), name="static") return app app = create_app()