ForcePilot/server/routers/base_router.py

246 lines
8.6 KiB
Python
Raw Normal View History

import os
import yaml
import asyncio
import requests
from pathlib import Path
from fastapi import Request, Body, Depends, HTTPException
2024-10-02 20:11:28 +08:00
from fastapi import APIRouter
from src import config, knowledge_base, graph_base
from server.utils.auth_middleware import get_admin_user, get_superadmin_user
from server.models.user_model import User
from src.utils.logging_config import logger
2024-10-02 20:11:28 +08:00
2025-05-24 11:29:45 +08:00
base = APIRouter()
def load_info_config():
"""加载信息配置文件"""
try:
# 配置文件路径
config_path = Path("src/static/info.local.yaml")
# 检查文件是否存在
if not config_path.exists():
logger.debug(f"The config file {config_path} does not exist, using default config")
return get_default_info_config()
# 读取配置文件
2025-06-23 10:32:01 +08:00
with open(config_path, encoding='utf-8') as file:
config = yaml.safe_load(file)
return config
except Exception as e:
logger.error(f"Failed to load info config: {e}")
return get_default_info_config()
def get_default_info_config():
"""获取默认信息配置"""
return {
"organization": {
2025-06-23 10:32:01 +08:00
"name": "江南语析",
"short_name": "语析",
"logo": "/favicon.svg",
"avatar": "/avatar.jpg"
},
"branding": {
"title": "Yuxi-Know",
"subtitle": "大模型驱动的知识库管理工具",
"description": "结合知识库与知识图谱,提供更准确、更全面的回答"
},
"features": [
"📚 灵活知识库",
"🕸️ 知识图谱集成",
"🤖 多模型支持"
],
"footer": {
2025-06-23 10:32:01 +08:00
"copyright": "© 江南语析 2025 [WIP] v0.12.138"
}
}
2024-10-02 20:11:28 +08:00
@base.get("/")
async def route_index():
return {"message": "You Got It!"}
@base.get("/health")
async def health_check():
"""简单的健康检查接口"""
return {"status": "ok", "message": "服务正常运行"}
2024-10-02 20:11:28 +08:00
@base.get("/config")
def get_config(current_user: User = Depends(get_admin_user)):
return config.dump_config()
2024-10-02 20:11:28 +08:00
@base.post("/config")
async def update_config(
key = Body(...),
value = Body(...),
current_user: User = Depends(get_admin_user)
) -> dict:
2025-03-16 01:11:13 +08:00
config[key] = value
config.save()
return config.dump_config()
2024-10-02 20:11:28 +08:00
2025-05-05 19:40:23 +08:00
@base.post("/config/update")
async def update_config_item(
items: dict = Body(...),
current_user: User = Depends(get_admin_user)
) -> dict:
config.update(items)
config.save()
return config.dump_config()
2024-10-02 20:11:28 +08:00
@base.post("/restart")
async def restart(current_user: User = Depends(get_superadmin_user)):
graph_base.start()
2024-10-02 20:11:28 +08:00
return {"message": "Restarted!"}
@base.get("/log")
def get_log(current_user: User = Depends(get_admin_user)):
2024-10-02 20:11:28 +08:00
from src.utils.logging_config import LOG_FILE
from collections import deque
2025-05-24 11:29:45 +08:00
with open(LOG_FILE) as f:
2024-10-02 20:11:28 +08:00
last_lines = deque(f, maxlen=1000)
log = ''.join(last_lines)
2025-02-28 00:38:59 +08:00
return {"log": log, "message": "success", "log_file": LOG_FILE}
2024-10-02 20:11:28 +08:00
@base.get("/info")
async def get_info_config():
"""获取系统信息配置(公开接口,无需认证)"""
try:
config = load_info_config()
return {
"success": True,
"data": config
}
except Exception as e:
logger.error(f"获取信息配置失败: {e}")
raise HTTPException(status_code=500, detail="获取信息配置失败")
@base.get("/info/reload")
async def reload_info_config():
"""重新加载信息配置(管理员接口)"""
# 注:这里暂时不添加权限验证,后续可以根据需要添加
try:
config = load_info_config()
return {
"success": True,
"message": "配置重新加载成功",
"data": config
}
except Exception as e:
logger.error(f"重新加载信息配置失败: {e}")
raise HTTPException(status_code=500, detail="重新加载信息配置失败")
@base.get("/ocr/health")
async def check_ocr_services_health(current_user: User = Depends(get_admin_user)):
"""
检查所有OCR服务的健康状态
返回各个OCR服务的可用性信息
"""
health_status = {
"rapid_ocr": {"status": "unknown", "message": ""},
"mineru_ocr": {"status": "unknown", "message": ""},
"paddlex_ocr": {"status": "unknown", "message": ""}
}
# 检查 RapidOCR (ONNX) 模型
try:
model_dir = os.path.join(os.getenv("MODEL_DIR", ""), "SWHL/RapidOCR")
det_model_path = os.path.join(model_dir, "PP-OCRv4/ch_PP-OCRv4_det_infer.onnx")
rec_model_path = os.path.join(model_dir, "PP-OCRv4/ch_PP-OCRv4_rec_infer.onnx")
if os.path.exists(model_dir) and os.path.exists(det_model_path) and os.path.exists(rec_model_path):
# 尝试初始化RapidOCR
from rapidocr_onnxruntime import RapidOCR
test_ocr = RapidOCR(det_box_thresh=0.3, det_model_path=det_model_path, rec_model_path=rec_model_path)
health_status["rapid_ocr"]["status"] = "healthy"
health_status["rapid_ocr"]["message"] = "RapidOCR模型已加载"
else:
health_status["rapid_ocr"]["status"] = "unavailable"
health_status["rapid_ocr"]["message"] = f"模型文件不存在: {model_dir}"
except Exception as e:
health_status["rapid_ocr"]["status"] = "error"
health_status["rapid_ocr"]["message"] = f"RapidOCR初始化失败: {str(e)}"
# 检查 MinerU OCR 服务
try:
mineru_uri = os.getenv("MINERU_OCR_URI", "http://localhost:30000")
health_url = f"{mineru_uri}/health"
response = requests.get(health_url, timeout=5)
if response.status_code == 200:
health_status["mineru_ocr"]["status"] = "healthy"
health_status["mineru_ocr"]["message"] = f"MinerU服务运行正常 ({mineru_uri})"
else:
health_status["mineru_ocr"]["status"] = "unhealthy"
health_status["mineru_ocr"]["message"] = f"MinerU服务响应异常: {response.status_code}"
except requests.exceptions.ConnectionError:
health_status["mineru_ocr"]["status"] = "unavailable"
health_status["mineru_ocr"]["message"] = "MinerU服务无法连接请检查服务是否启动"
except requests.exceptions.Timeout:
health_status["mineru_ocr"]["status"] = "timeout"
health_status["mineru_ocr"]["message"] = "MinerU服务连接超时"
except Exception as e:
health_status["mineru_ocr"]["status"] = "error"
health_status["mineru_ocr"]["message"] = f"MinerU服务检查失败: {str(e)}"
# 检查 PaddleX OCR 服务
try:
paddlex_uri = os.getenv("PADDLEX_URI", "http://localhost:8080")
health_url = f"{paddlex_uri}/health"
response = requests.get(health_url, timeout=5)
if response.status_code == 200:
health_status["paddlex_ocr"]["status"] = "healthy"
health_status["paddlex_ocr"]["message"] = f"PaddleX服务运行正常 ({paddlex_uri})"
else:
health_status["paddlex_ocr"]["status"] = "unhealthy"
health_status["paddlex_ocr"]["message"] = f"PaddleX服务响应异常: {response.status_code}"
except requests.exceptions.ConnectionError:
health_status["paddlex_ocr"]["status"] = "unavailable"
health_status["paddlex_ocr"]["message"] = "PaddleX服务无法连接请检查服务是否启动"
except requests.exceptions.Timeout:
health_status["paddlex_ocr"]["status"] = "timeout"
health_status["paddlex_ocr"]["message"] = "PaddleX服务连接超时"
except Exception as e:
health_status["paddlex_ocr"]["status"] = "error"
health_status["paddlex_ocr"]["message"] = f"PaddleX服务检查失败: {str(e)}"
# 计算整体健康状态
overall_status = "healthy" if any(svc["status"] == "healthy" for svc in health_status.values()) else "unhealthy"
return {
"overall_status": overall_status,
"services": health_status,
"message": "OCR服务健康检查完成"
}
@base.get("/ocr/stats")
async def get_ocr_stats(current_user: User = Depends(get_admin_user)):
"""
获取OCR服务使用统计信息
返回各个OCR服务的处理统计和性能指标
"""
try:
from src.plugins._ocr import get_ocr_stats
stats = get_ocr_stats()
return {
"status": "success",
"stats": stats,
"message": "OCR统计信息获取成功"
}
except Exception as e:
logger.error(f"获取OCR统计信息失败: {str(e)}")
return {
"status": "error",
"stats": {},
"message": f"获取OCR统计信息失败: {str(e)}"
}
2024-10-02 20:11:28 +08:00