ForcePilot/server/routers/base_router.py

246 lines
8.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import os
import yaml
import asyncio
import requests
from pathlib import Path
from fastapi import Request, Body, Depends, HTTPException
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
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()
# 读取配置文件
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": {
"name": "江南语析",
"short_name": "语析",
"logo": "/favicon.svg",
"avatar": "/avatar.jpg"
},
"branding": {
"title": "Yuxi-Know",
"subtitle": "大模型驱动的知识库管理工具",
"description": "结合知识库与知识图谱,提供更准确、更全面的回答"
},
"features": [
"📚 灵活知识库",
"🕸️ 知识图谱集成",
"🤖 多模型支持"
],
"footer": {
"copyright": "© 江南语析 2025 [WIP] v0.12.138"
}
}
@base.get("/")
async def route_index():
return {"message": "You Got It!"}
@base.get("/health")
async def health_check():
"""简单的健康检查接口"""
return {"status": "ok", "message": "服务正常运行"}
@base.get("/config")
def get_config(current_user: User = Depends(get_admin_user)):
return config.dump_config()
@base.post("/config")
async def update_config(
key = Body(...),
value = Body(...),
current_user: User = Depends(get_admin_user)
) -> dict:
config[key] = value
config.save()
return config.dump_config()
@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()
@base.post("/restart")
async def restart(current_user: User = Depends(get_superadmin_user)):
graph_base.start()
return {"message": "Restarted!"}
@base.get("/log")
def get_log(current_user: User = Depends(get_admin_user)):
from src.utils.logging_config import LOG_FILE
from collections import deque
with open(LOG_FILE) as f:
last_lines = deque(f, maxlen=1000)
log = ''.join(last_lines)
return {"log": log, "message": "success", "log_file": LOG_FILE}
@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)}"
}