report_parse.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. from fastapi import APIRouter, HTTPException
  2. from pydantic import BaseModel
  3. from typing import Optional, Any
  4. from app.agents.report_parse_agent import ReportParseAgent
  5. import logging
  6. import os
  7. import json
  8. logger = logging.getLogger(__name__)
  9. router = APIRouter(prefix="/api/v1", tags=["report_parse"])
  10. _agent = None
  11. def get_agent():
  12. global _agent
  13. if _agent is None:
  14. _agent = ReportParseAgent()
  15. return _agent
  16. class ParseRequest(BaseModel):
  17. file_path: str
  18. family_id: Optional[int] = None
  19. user_id: Optional[int] = None
  20. indicators: Optional[list[str]] = None
  21. class ParseResponse(BaseModel):
  22. code: int = 200
  23. message: str = "ok"
  24. data: dict = {}
  25. @router.post("/report/parse", response_model=ParseResponse)
  26. async def parse_report(req: ParseRequest):
  27. if not os.path.exists(req.file_path):
  28. raise HTTPException(status_code=400, detail=f"文件不存在: {req.file_path}")
  29. logger.info("report_parse: file_path=%s family_id=%s user_id=%s",
  30. req.file_path, req.family_id, req.user_id)
  31. agent = get_agent()
  32. try:
  33. result = await agent.parse(req.file_path, req.indicators)
  34. return ParseResponse(data=result)
  35. except Exception as e:
  36. logger.error("报告解析失败: %s", e, exc_info=True)
  37. return ParseResponse(code=500, message=f"解析失败: {str(e)}", data={})
  38. class GenericParseRequest(BaseModel):
  39. file_path: str
  40. extra_context: Optional[dict] = None
  41. class GenericParseResponse(BaseModel):
  42. code: int = 200
  43. message: str = "ok"
  44. data: dict = {}
  45. @router.post("/report/parse-generic", response_model=GenericParseResponse)
  46. async def parse_report_generic(req: GenericParseRequest):
  47. """通用报告 LLM 兜底解析。
  48. 适用于指纹检测未匹配的未知类型报告。
  49. 直接交给 LLM 提取结构化数据,不经过算法预解析。
  50. """
  51. if not os.path.exists(req.file_path):
  52. raise HTTPException(status_code=400, detail=f"文件不存在: {req.file_path}")
  53. logger.info("report_parse_generic: file_path=%s", req.file_path)
  54. agent = get_agent()
  55. try:
  56. result = await agent.parse_generic(req.file_path, req.extra_context)
  57. return GenericParseResponse(data=result)
  58. except Exception as e:
  59. logger.error("通用报告解析失败: %s", e, exc_info=True)
  60. return GenericParseResponse(code=500, message=f"解析失败: {str(e)}", data={})
  61. class TypedParseRequest(BaseModel):
  62. file_path: str
  63. report_type: str = "auto"
  64. extra_context: Optional[dict] = None
  65. indicators: Optional[list[str]] = None
  66. class TypedParseResponse(BaseModel):
  67. code: int = 200
  68. message: str = "ok"
  69. data: dict = {}
  70. @router.post("/report/parse-typed", response_model=TypedParseResponse)
  71. async def parse_report_typed(req: TypedParseRequest):
  72. """按报告类型解析。
  73. 支持的报告类型:
  74. - auto: 自动检测(默认)
  75. - brain_status: 脑状态测量报告
  76. - cognitive_aptitude: 先天智力潜能/皮纹学测评报告
  77. - scanned_image: 扫描图片 PDF(无文字层,需多模态 LLM)
  78. - generic: 通用 LLM 解析
  79. - gut_flora: 肠道菌群报告(算法解析 + LLM 兜底)
  80. """
  81. if not os.path.exists(req.file_path):
  82. raise HTTPException(status_code=400, detail=f"文件不存在: {req.file_path}")
  83. logger.info("report_parse_typed: file_path=%s type=%s",
  84. req.file_path, req.report_type)
  85. agent = get_agent()
  86. try:
  87. result = await agent.parse_by_type(req.file_path, req.report_type, req.extra_context, req.indicators)
  88. return TypedParseResponse(data=result)
  89. except Exception as e:
  90. logger.error("类型报告解析失败: %s", e, exc_info=True)
  91. return TypedParseResponse(code=500, message=f"解析失败: {str(e)}", data={})
  92. class TypedParseRequest2(BaseModel):
  93. file_path: str
  94. @router.post("/report/detect-type", response_model=TypedParseResponse)
  95. async def detect_report_type(req: TypedParseRequest2):
  96. """检测报告类型,返回检测结果(不执行解析)。"""
  97. if not os.path.exists(req.file_path):
  98. raise HTTPException(status_code=400, detail=f"文件不存在: {req.file_path}")
  99. agent = get_agent()
  100. try:
  101. detected = agent._detect_report_type(req.file_path)
  102. return TypedParseResponse(data={"detectedType": detected})
  103. except Exception as e:
  104. logger.error("报告类型检测失败: %s", e, exc_info=True)
  105. return TypedParseResponse(code=500, message=f"检测失败: {str(e)}", data={})