"""Define API routes for compliance.""" from __future__ import annotations import asyncio import json from pathlib import Path from typing import AsyncGenerator from fastapi import APIRouter, File, UploadFile from fastapi.responses import StreamingResponse from app.schemas.compliance import ( AnalyzeResponse, ComplianceChatRequest, ) from app.services.mock_data import ( generate_task_id, get_mock_compliance_result, get_mock_compliance_chat_response, ) # Keep route handlers close to their transport-layer wiring for easier auditing. router = APIRouter(prefix="/compliance", tags=["合规分析"]) # Keep route handlers close to their transport-layer wiring for easier auditing. tasks_store: dict[str, dict] = {} # Store uploaded compliance files inside the local backend data directory. RAW_DATA_DIR = Path(__file__).resolve().parents[3] / "data" / "raw" @router.post("/analyze", response_model=AnalyzeResponse) async def analyze_document(file: UploadFile = File(...)): """Handle analyze document.""" # Keep route handlers close to their transport-layer wiring for easier auditing. task_id = generate_task_id() # Keep route handlers close to their transport-layer wiring for easier auditing. RAW_DATA_DIR.mkdir(parents=True, exist_ok=True) file_path = RAW_DATA_DIR / f"compliance_{task_id}_{file.filename}" content = await file.read() with file_path.open("wb") as f: f.write(content) # Keep route handlers close to their transport-layer wiring for easier auditing. tasks_store[task_id] = { "task_id": task_id, "file_path": str(file_path), "status": "processing", "result": None, } # Keep route handlers close to their transport-layer wiring for easier auditing. # Keep route handlers close to their transport-layer wiring for easier auditing. tasks_store[task_id]["status"] = "completed" tasks_store[task_id]["result"] = get_mock_compliance_result(task_id) return AnalyzeResponse(task_id=task_id) @router.get("/result/{task_id}") async def get_result(task_id: str): """Return result.""" if task_id not in tasks_store: # Keep route handlers close to their transport-layer wiring for easier auditing. return get_mock_compliance_result(task_id) task = tasks_store[task_id] if task["status"] == "processing": return {"status": "processing", "message": "分析进行中"} return task["result"] @router.post("/chat/{segment_id}") async def compliance_chat(segment_id: int, request: ComplianceChatRequest): """Handle compliance chat.""" # Keep route handlers close to their transport-layer wiring for easier auditing. intent_map = { 1: "车身结构设计", 2: "动力系统配置", 3: "安全配置设计", } intent = intent_map.get(segment_id, "车身结构设计") async def generate() -> AsyncGenerator[str, None]: # Keep route handlers close to their transport-layer wiring for easier auditing. """Handle generate.""" response = get_mock_compliance_chat_response(intent, request.query) # Keep route handlers close to their transport-layer wiring for easier auditing. sentences = response.split("\n\n") for sentence in sentences: if sentence.strip(): chunks = sentence.split("\n") for chunk in chunks: if chunk.strip(): await asyncio.sleep(0.05) yield ( "event: message\n" f"data: {json.dumps({'type': 'chunk', 'text': chunk + chr(10)}, ensure_ascii=False)}\n\n" ) yield f"event: message\ndata: {json.dumps({'type': 'done'}, ensure_ascii=False)}\n\n" return StreamingResponse( generate(), media_type="text/event-stream", headers={"Cache-Control": "no-cache", "Connection": "keep-alive", "X-Accel-Buffering": "no"}, )