完善系统健康面板并接入AI运行态观测
This commit is contained in:
@@ -15,6 +15,7 @@ import toml
|
||||
from utils.markdown_to_image import get_md2img_health_snapshot, warmup_md2img_browser_sync
|
||||
from utils.ai.llm_registry import LLMRegistry
|
||||
from base.plugin_common.plugin_interface import PluginStatus
|
||||
from utils.ai.unified_llm import UnifiedLLMClient
|
||||
|
||||
# 创建系统信息蓝图
|
||||
system_bp = Blueprint('system', __name__)
|
||||
@@ -403,6 +404,33 @@ def api_system_health_summary():
|
||||
# 错误数量直接复用现有统计库,避免为了首页卡片再单独写一套 SQL。
|
||||
_, recent_error_count = server.stats_db.get_error_logs(days=1, page=1, limit=1)
|
||||
|
||||
# 基础设施健康:
|
||||
# 1. MySQL 用最轻量的 SELECT 1 做可用性探测;
|
||||
# 2. Redis 用 PING 验证连接池当前是否可拿到可用连接;
|
||||
# 3. 即使探测失败也只反馈到看板,不影响主接口整体返回。
|
||||
mysql_status = "healthy"
|
||||
mysql_summary = "连接正常"
|
||||
try:
|
||||
mysql_conn = server.db_manager.get_mysql_connection()
|
||||
try:
|
||||
with mysql_conn.cursor() as cursor:
|
||||
cursor.execute("SELECT 1")
|
||||
cursor.fetchone()
|
||||
finally:
|
||||
mysql_conn.close()
|
||||
except Exception as mysql_error:
|
||||
mysql_status = "danger"
|
||||
mysql_summary = f"MySQL 探测失败: {mysql_error}"
|
||||
|
||||
redis_status = "healthy"
|
||||
redis_summary = "连接正常"
|
||||
try:
|
||||
redis_conn = server.db_manager.get_redis_connection()
|
||||
redis_conn.ping()
|
||||
except Exception as redis_error:
|
||||
redis_status = "danger"
|
||||
redis_summary = f"Redis 探测失败: {redis_error}"
|
||||
|
||||
# md2img 健康快照已经有现成实现,这里只做聚合,不主动预热运行时。
|
||||
md2img_snapshot = get_md2img_health_snapshot(ensure_runtime=False) or {}
|
||||
browser_ready = bool(
|
||||
@@ -452,6 +480,28 @@ def api_system_health_summary():
|
||||
md2img_status = "danger"
|
||||
md2img_summary = "运行时未就绪,相关转图能力可能不可用"
|
||||
|
||||
# AI 运行态:
|
||||
# 1. 统一从 UnifiedLLMClient 最近调用窗口读取,避免各插件单独维护监控数据;
|
||||
# 2. 若当前窗口还没有调用记录,就明确返回“暂无调用”,避免误判成异常。
|
||||
ai_runtime = UnifiedLLMClient.get_runtime_snapshot()
|
||||
ai_total_calls = int(ai_runtime.get("total_calls") or 0)
|
||||
ai_failed_calls = int(ai_runtime.get("failed_calls") or 0)
|
||||
if ai_total_calls <= 0:
|
||||
ai_status = "warning"
|
||||
ai_summary = "最近窗口内暂无统一 LLM 调用记录"
|
||||
elif ai_failed_calls > 0:
|
||||
ai_status = "warning"
|
||||
ai_summary = (
|
||||
f"最近 {ai_total_calls} 次调用中失败 {ai_failed_calls} 次,"
|
||||
f"平均耗时 {ai_runtime.get('avg_latency_ms', 0)}ms"
|
||||
)
|
||||
else:
|
||||
ai_status = "healthy"
|
||||
ai_summary = (
|
||||
f"最近 {ai_total_calls} 次调用全部成功,"
|
||||
f"平均耗时 {ai_runtime.get('avg_latency_ms', 0)}ms"
|
||||
)
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"data": {
|
||||
@@ -473,6 +523,27 @@ def api_system_health_summary():
|
||||
"recent_24h_count": recent_error_count,
|
||||
"summary": error_summary,
|
||||
},
|
||||
"infrastructure": {
|
||||
"status": "healthy" if mysql_status == "healthy" and redis_status == "healthy" else "danger",
|
||||
"summary": (
|
||||
"MySQL / Redis 均正常"
|
||||
if mysql_status == "healthy" and redis_status == "healthy"
|
||||
else "存在基础设施连接异常"
|
||||
),
|
||||
"mysql": {
|
||||
"status": mysql_status,
|
||||
"summary": mysql_summary,
|
||||
},
|
||||
"redis": {
|
||||
"status": redis_status,
|
||||
"summary": redis_summary,
|
||||
},
|
||||
},
|
||||
"ai_runtime": {
|
||||
"status": ai_status,
|
||||
"summary": ai_summary,
|
||||
**ai_runtime,
|
||||
},
|
||||
"md2img": {
|
||||
"status": md2img_status,
|
||||
"healthy": md2img_healthy,
|
||||
|
||||
@@ -355,6 +355,26 @@
|
||||
recent_24h_count: 0,
|
||||
summary: '加载中...'
|
||||
},
|
||||
infrastructure: {
|
||||
status: 'warning',
|
||||
summary: '加载中...',
|
||||
mysql: {
|
||||
status: 'warning',
|
||||
summary: '加载中...'
|
||||
},
|
||||
redis: {
|
||||
status: 'warning',
|
||||
summary: '加载中...'
|
||||
}
|
||||
},
|
||||
ai_runtime: {
|
||||
status: 'warning',
|
||||
total_calls: 0,
|
||||
failed_calls: 0,
|
||||
avg_latency_ms: 0,
|
||||
summary: '加载中...',
|
||||
last_call: {}
|
||||
},
|
||||
md2img: {
|
||||
status: 'warning',
|
||||
healthy: false,
|
||||
@@ -401,6 +421,8 @@
|
||||
const robot = this.healthSummary.robot || {};
|
||||
const plugins = this.healthSummary.plugins || {};
|
||||
const errors = this.healthSummary.errors || {};
|
||||
const infrastructure = this.healthSummary.infrastructure || {};
|
||||
const aiRuntime = this.healthSummary.ai_runtime || {};
|
||||
const md2img = this.healthSummary.md2img || {};
|
||||
return [
|
||||
{
|
||||
@@ -427,6 +449,22 @@
|
||||
summary: errors.summary || '暂无状态',
|
||||
extra: '统计窗口:近 24 小时'
|
||||
},
|
||||
{
|
||||
key: 'infrastructure',
|
||||
title: '基础设施',
|
||||
status: infrastructure.status || 'warning',
|
||||
value: infrastructure.status === 'healthy' ? '正常' : '异常',
|
||||
summary: infrastructure.summary || '暂无状态',
|
||||
extra: `MySQL:${((infrastructure.mysql || {}).status === 'healthy') ? '正常' : '异常'} / Redis:${((infrastructure.redis || {}).status === 'healthy') ? '正常' : '异常'}`
|
||||
},
|
||||
{
|
||||
key: 'ai_runtime',
|
||||
title: 'AI 运行态',
|
||||
status: aiRuntime.status || 'warning',
|
||||
value: `${aiRuntime.avg_latency_ms || 0} ms`,
|
||||
summary: aiRuntime.summary || '暂无状态',
|
||||
extra: `最近调用 ${aiRuntime.total_calls || 0} 次,失败 ${aiRuntime.failed_calls || 0} 次`
|
||||
},
|
||||
{
|
||||
key: 'md2img',
|
||||
title: 'Markdown 转图',
|
||||
@@ -978,7 +1016,7 @@
|
||||
|
||||
.health-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 16px;
|
||||
margin-top: 18px;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user