初始化
This commit is contained in:
3
app/api/__init__.py
Normal file
3
app/api/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from .routes import api_router
|
||||
|
||||
__all__ = ["api_router"]
|
||||
13
app/api/routes/__init__.py
Normal file
13
app/api/routes/__init__.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from fastapi import APIRouter
|
||||
from .docs import router as docs_router
|
||||
from .rag import router as rag_router
|
||||
from .compliance import router as compliance_router
|
||||
from .status import router as status_router
|
||||
|
||||
api_router = APIRouter()
|
||||
api_router.include_router(docs_router)
|
||||
api_router.include_router(rag_router)
|
||||
api_router.include_router(compliance_router)
|
||||
api_router.include_router(status_router)
|
||||
|
||||
__all__ = ["api_router"]
|
||||
96
app/api/routes/compliance.py
Normal file
96
app/api/routes/compliance.py
Normal file
@@ -0,0 +1,96 @@
|
||||
from fastapi import APIRouter, UploadFile, File, HTTPException
|
||||
from sse_starlette.sse import EventSourceResponse
|
||||
import uuid
|
||||
import os
|
||||
import json
|
||||
import asyncio
|
||||
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,
|
||||
)
|
||||
|
||||
router = APIRouter(prefix="/compliance", tags=["合规分析"])
|
||||
|
||||
# 临时存储分析任务
|
||||
tasks_store: dict[str, dict] = {}
|
||||
|
||||
|
||||
@router.post("/analyze", response_model=AnalyzeResponse)
|
||||
async def analyze_document(file: UploadFile = File(...)):
|
||||
"""上传设计方案进行分析"""
|
||||
# 生成任务ID
|
||||
task_id = generate_task_id()
|
||||
|
||||
# 保存文件
|
||||
raw_dir = "/airegulation/demo-mao/backend/data/raw"
|
||||
os.makedirs(raw_dir, exist_ok=True)
|
||||
file_path = os.path.join(raw_dir, f"compliance_{task_id}_{file.filename}")
|
||||
|
||||
content = await file.read()
|
||||
with open(file_path, "wb") as f:
|
||||
f.write(content)
|
||||
|
||||
# 记录任务
|
||||
tasks_store[task_id] = {
|
||||
"task_id": task_id,
|
||||
"file_path": file_path,
|
||||
"status": "processing",
|
||||
"result": None,
|
||||
}
|
||||
|
||||
# 模拟异步处理完成(立即返回结果)
|
||||
# 实际应用中这应该是后台任务
|
||||
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):
|
||||
"""获取分析结果"""
|
||||
if task_id not in tasks_store:
|
||||
# 如果任务ID不存在,返回默认mock结果
|
||||
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):
|
||||
"""针对段落进行合规对话"""
|
||||
# 根据segment_id获取对应的intent
|
||||
intent_map = {
|
||||
1: "车身结构设计",
|
||||
2: "动力系统配置",
|
||||
3: "安全配置设计",
|
||||
}
|
||||
intent = intent_map.get(segment_id, "车身结构设计")
|
||||
|
||||
async def generate():
|
||||
# 获取预设响应
|
||||
response = get_mock_compliance_chat_response(intent, request.query)
|
||||
|
||||
# 流式输出响应
|
||||
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", "data": json.dumps({"type": "chunk", "text": chunk + "\n"})}
|
||||
|
||||
yield {"event": "message", "data": json.dumps({"type": "done"})}
|
||||
|
||||
return EventSourceResponse(generate())
|
||||
115
app/api/routes/docs.py
Normal file
115
app/api/routes/docs.py
Normal file
@@ -0,0 +1,115 @@
|
||||
from fastapi import APIRouter, UploadFile, File, HTTPException
|
||||
import os
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from app.schemas.doc import (
|
||||
DocumentUploadResponse,
|
||||
DocumentListResponse,
|
||||
DocumentInfo,
|
||||
ParseResponse,
|
||||
EmbedResponse,
|
||||
)
|
||||
from app.services.mock_data import get_mock_documents, generate_doc_id
|
||||
|
||||
router = APIRouter(prefix="/docs", tags=["文档管理"])
|
||||
|
||||
# 临时存储文档信息(包含预设的mock文档)
|
||||
documents_store: dict[str, dict] = {}
|
||||
|
||||
# 初始化时加载mock文档
|
||||
for doc in get_mock_documents():
|
||||
documents_store[doc["id"]] = doc
|
||||
|
||||
|
||||
@router.post("/upload", response_model=DocumentUploadResponse)
|
||||
async def upload_document(file: UploadFile = File(...)):
|
||||
"""上传法规文档"""
|
||||
# 检查文件格式
|
||||
allowed_ext = [".pdf", ".docx", ".doc", ".txt"]
|
||||
ext = os.path.splitext(file.filename)[1].lower()
|
||||
if ext not in allowed_ext:
|
||||
raise HTTPException(400, f"Unsupported file format: {ext}")
|
||||
|
||||
# 生成文档ID
|
||||
doc_id = generate_doc_id()
|
||||
|
||||
# 保存文件
|
||||
raw_dir = "/airegulation/demo-mao/backend/data/raw"
|
||||
os.makedirs(raw_dir, exist_ok=True)
|
||||
file_path = os.path.join(raw_dir, f"{doc_id}_{file.filename}")
|
||||
|
||||
content = await file.read()
|
||||
with open(file_path, "wb") as f:
|
||||
f.write(content)
|
||||
|
||||
# 记录文档信息
|
||||
documents_store[doc_id] = {
|
||||
"id": doc_id,
|
||||
"name": file.filename,
|
||||
"path": file_path,
|
||||
"size": len(content),
|
||||
"status": "uploaded",
|
||||
"chunks": 0,
|
||||
"created_at": datetime.now(),
|
||||
}
|
||||
|
||||
return DocumentUploadResponse(
|
||||
doc_id=doc_id,
|
||||
filename=file.filename,
|
||||
size=len(content),
|
||||
)
|
||||
|
||||
|
||||
@router.get("/list", response_model=DocumentListResponse)
|
||||
async def list_documents():
|
||||
"""获取已索引文档列表"""
|
||||
docs = [
|
||||
DocumentInfo(
|
||||
id=d["id"],
|
||||
name=d["name"],
|
||||
chunks=d["chunks"],
|
||||
status=d["status"],
|
||||
created_at=d.get("created_at"),
|
||||
)
|
||||
for d in documents_store.values()
|
||||
]
|
||||
return DocumentListResponse(docs=docs)
|
||||
|
||||
|
||||
@router.post("/parse/{doc_id}", response_model=ParseResponse)
|
||||
async def parse_document(doc_id: str):
|
||||
"""解析文档并分块"""
|
||||
if doc_id not in documents_store:
|
||||
raise HTTPException(404, "Document not found")
|
||||
|
||||
doc = documents_store[doc_id]
|
||||
# 模拟解析逻辑
|
||||
doc["status"] = "parsed"
|
||||
# 根据文件大小计算chunks数量
|
||||
file_size = doc.get("size", 100000)
|
||||
doc["chunks"] = max(20, file_size // 8000)
|
||||
|
||||
return ParseResponse(doc_id=doc_id, chunks=doc["chunks"])
|
||||
|
||||
|
||||
@router.post("/embed/{doc_id}", response_model=EmbedResponse)
|
||||
async def embed_document(doc_id: str):
|
||||
"""嵌入并存入向量库"""
|
||||
if doc_id not in documents_store:
|
||||
raise HTTPException(404, "Document not found")
|
||||
|
||||
doc = documents_store[doc_id]
|
||||
# 模拟嵌入逻辑
|
||||
doc["status"] = "indexed"
|
||||
|
||||
return EmbedResponse(doc_id=doc_id, vectors=doc["chunks"])
|
||||
|
||||
|
||||
@router.delete("/delete/{doc_id}")
|
||||
async def delete_document(doc_id: str):
|
||||
"""删除文档"""
|
||||
if doc_id not in documents_store:
|
||||
raise HTTPException(404, "Document not found")
|
||||
|
||||
del documents_store[doc_id]
|
||||
return {"success": True}
|
||||
74
app/api/routes/rag.py
Normal file
74
app/api/routes/rag.py
Normal file
@@ -0,0 +1,74 @@
|
||||
from fastapi import APIRouter
|
||||
from sse_starlette.sse import EventSourceResponse
|
||||
from app.schemas.rag import RagChatRequest, QuickQuestionsResponse, QuickQuestion
|
||||
from app.services.mock_data import (
|
||||
get_mock_quick_questions,
|
||||
get_mock_retrieval,
|
||||
get_mock_rag_answer,
|
||||
)
|
||||
import json
|
||||
import asyncio
|
||||
|
||||
router = APIRouter(prefix="/rag", tags=["RAG问答"])
|
||||
|
||||
|
||||
@router.post("/chat")
|
||||
async def rag_chat(request: RagChatRequest):
|
||||
"""SSE流式问答"""
|
||||
|
||||
async def generate():
|
||||
# 发送检索开始事件
|
||||
yield {"event": "message", "data": json.dumps({"type": "retrieving"})}
|
||||
|
||||
# 模拟检索延迟
|
||||
await asyncio.sleep(0.3)
|
||||
|
||||
# 执行检索
|
||||
docs = get_mock_retrieval(request.query, top_k=request.top_k)
|
||||
|
||||
retrieved_data = [
|
||||
{
|
||||
"id": d["id"],
|
||||
"score": d["score"],
|
||||
"preview": d["preview"],
|
||||
"doc_name": d.get("doc_name", ""),
|
||||
"clause": d.get("clause", ""),
|
||||
}
|
||||
for d in docs
|
||||
]
|
||||
yield {"event": "message", "data": json.dumps({"type": "retrieved", "docs": retrieved_data})}
|
||||
|
||||
# 发送生成开始事件
|
||||
yield {"event": "message", "data": json.dumps({"type": "generating", "text": "正在生成答案..."})}
|
||||
|
||||
# 模拟生成延迟
|
||||
await asyncio.sleep(0.2)
|
||||
|
||||
# 获取预设答案
|
||||
answer = get_mock_rag_answer(request.query)
|
||||
|
||||
# 流式输出答案(按句子分割)
|
||||
sentences = answer.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", "data": json.dumps({"type": "chunk", "text": chunk + "\n"})}
|
||||
|
||||
# 发送完成事件
|
||||
yield {"event": "message", "data": json.dumps({"type": "done"})}
|
||||
|
||||
return EventSourceResponse(generate())
|
||||
|
||||
|
||||
@router.get("/quick-questions", response_model=QuickQuestionsResponse)
|
||||
async def get_quick_questions():
|
||||
"""获取预设快捷问题"""
|
||||
questions = [
|
||||
QuickQuestion(id=q["id"], question=q["question"], category=q["category"])
|
||||
for q in get_mock_quick_questions()
|
||||
]
|
||||
return QuickQuestionsResponse(questions=questions)
|
||||
28
app/api/routes/status.py
Normal file
28
app/api/routes/status.py
Normal file
@@ -0,0 +1,28 @@
|
||||
from fastapi import APIRouter
|
||||
from app.core.config import settings
|
||||
from app.services.mock_data import MOCK_SYSTEM_STATS, MOCK_SYSTEM_CONFIG
|
||||
|
||||
router = APIRouter(prefix="/status", tags=["系统状态"])
|
||||
|
||||
|
||||
@router.get("/stats")
|
||||
async def get_stats():
|
||||
"""获取系统统计"""
|
||||
# 返回预设统计数据
|
||||
return MOCK_SYSTEM_STATS
|
||||
|
||||
|
||||
@router.get("/config")
|
||||
async def get_config():
|
||||
"""获取当前配置"""
|
||||
return MOCK_SYSTEM_CONFIG
|
||||
|
||||
|
||||
@router.get("/milvus/health")
|
||||
async def milvus_health():
|
||||
"""Milvus健康检查"""
|
||||
# 模拟连接状态(假数据模式下始终返回连接成功)
|
||||
return {
|
||||
"connected": True,
|
||||
"collections": ["vehicle_regulations"],
|
||||
}
|
||||
Reference in New Issue
Block a user