import json import os from fastapi import APIRouter, UploadFile, File, Form, HTTPException from fastapi.responses import StreamingResponse import utils.util from api.handler_registry import HANDLER_REGISTRY from api.schema import ChatRequest from config.settings import JSON_CACHE_DIR, REPORT_OUTPUT_DIR, SERVER_HOST, PDF_SAVE_DIR from service.stream_service import format_sse_event, stream_calculation_sync, detect_intent, stream_review_sync from service.history_manager import fetch_all_sessions, fetch_session_detail, remove_session_file from service.pdf_parse_service import parse_pdf, get_latest_file router = APIRouter() @router.post("/api/vent/review/pdf") async def api_review( file: UploadFile = File(), session_id: str | None = Form(default=None), message : str | None = Form(default=None) ): if not file.filename.endswith(".pdf"): raise HTTPException(status_code=400, detail="仅支持PDF") return StreamingResponse( stream_review_sync(file,message,session_id), media_type="text/event-stream", headers={ "Cache-Control": "no-cache", "Connection": "keep-alive", "X-Accel-Buffering": "no" } ) @router.post("/api/vent/chat") async def vent_chat(req: ChatRequest): return StreamingResponse( stream_calculation_sync(req.message, req.session_id), media_type="text/event-stream", headers={ "Cache-Control": "no-cache", "Connection": "keep-alive" } ) @router.get("/health") async def health(): return {"status": "ok", "agent": "create_agent(LangGraph)"} @router.get("/api/history/session_list") async def list_sessions(): return {"code":200,"data":fetch_all_sessions()} @router.get("/api/history/detail/{session_id}") async def get_session_history(session_id:str): data = fetch_session_detail(session_id) return {"code":200,"data":data} @router.delete("/api/history/delete/{session_id}") async def remove_session(session_id: str): success = remove_session_file(session_id) if not success: raise HTTPException( status_code=404, detail=f"会话 {session_id} 不存在或删除失败" ) return {"code": 200, "message": "会话删除成功", "data": None} @router.get("/api/latest-json", summary="") async def get_latest_json(): """ 获取指定文件夹中修改时间最新的JSON文件并返回内容 """ hash_id = utils.util.get_user_setting("last_upload_file") latest_file_path = f"{JSON_CACHE_DIR}/{hash_id}.json" try: with open(latest_file_path, "r", encoding="utf-8") as f: data = json.load(f) except json.JSONDecodeError: raise HTTPException(status_code=500, detail="JSON文件格式错误") except Exception as e: raise HTTPException(status_code=500, detail=f"读取文件失败: {str(e)}") return {"code": 200, "message": "一切ok", "data": data} @router.get("/api/latest-pdf", summary="") async def get_latest_pdf(): hash_id = utils.util.get_user_setting("last_upload_file") pdf_filename = f"{hash_id}.pdf" url = f"{SERVER_HOST}/static/save_pdf/{pdf_filename}" return {"code": 200, "message": "一切ok", "data": url} @router.post("/api/vent/unified") async def unified_vent_api( # 表单参数 message: str = Form(...), session_id: str | None = Form(default=None), # 文件参数(可选) file: UploadFile | None = File(default=None) ): """ 配风管理统一入口:意图识别 + 自动分发业务 支持:纯文本对话 / PDF文件+文本审查 """ # 1. 基础参数校验 if not message.strip(): raise HTTPException(status_code=400, detail="指令内容不能为空") # 2. 意图识别 has_file = bool(file) intent_type = await detect_intent(session_id,message,has_file) # 3. 未知意图:使用stream_calculation_sync进行流式处理 if intent_type not in HANDLER_REGISTRY: headers = {"Cache-Control": "no-cache", "Connection": "keep-alive"} return StreamingResponse( stream_calculation_sync(message, session_id), media_type="text/event-stream", headers=headers ) # 4. 从注册表获取对应处理器 handler = HANDLER_REGISTRY[intent_type] # 5. 执行当前业务的专属参数校验 handler.validate(file) # 6. 生成流式响应(分两种场景调用) if handler.need_file: # 带文件:审查场景 stream_iter = handler.stream_func(file,message, session_id) headers = {"Cache-Control": "no-cache", "Connection": "keep-alive", "X-Accel-Buffering": "no"} else: # 无文件:计算场景 stream_iter = handler.stream_func(message, session_id) headers = {"Cache-Control": "no-cache", "Connection": "keep-alive"} return StreamingResponse(stream_iter, media_type="text/event-stream", headers=headers)