From 772f45cb2f857b8acee3b845641d7a2a0f1204ce Mon Sep 17 00:00:00 2001 From: liuwei Date: Wed, 8 Apr 2026 14:16:37 +0800 Subject: [PATCH] feat: separate global llm settings from monitor page --- admin/dashboard/blueprints/system.py | 6 + admin/dashboard/templates/base.html | 2 + admin/dashboard/templates/system_llm.html | 292 +++++++++++++++++++ admin/dashboard/templates/system_status.html | 285 ++---------------- 4 files changed, 318 insertions(+), 267 deletions(-) create mode 100644 admin/dashboard/templates/system_llm.html diff --git a/admin/dashboard/blueprints/system.py b/admin/dashboard/blueprints/system.py index a65216e..b2a0e81 100644 --- a/admin/dashboard/blueprints/system.py +++ b/admin/dashboard/blueprints/system.py @@ -67,6 +67,12 @@ def system_status(): return render_template('system_status.html', src_url=src) +@system_bp.route('/system_llm') +@login_required +def system_llm(): + return render_template('system_llm.html') + + # 页面路由 @system_bp.route('/wx_logs') @login_required diff --git a/admin/dashboard/templates/base.html b/admin/dashboard/templates/base.html index 4390b28..b4892c4 100644 --- a/admin/dashboard/templates/base.html +++ b/admin/dashboard/templates/base.html @@ -752,6 +752,7 @@ items: [ { label: '用户统计', path: '/users' }, { label: '资源监控', path: '/system_status' }, + { label: '全局配置', path: '/system_llm' }, { label: '文件浏览', path: '/file_browser' }, { label: '运行日志', path: '/wx_logs' }, { label: '错误日志', path: '/errors' } @@ -828,6 +829,7 @@ '12': '/virtual_group', '13': '/api_docs', '14': '/system_status', + '17': '/system_llm', '15': '/file_browser', '16': '/message_push' }; diff --git a/admin/dashboard/templates/system_llm.html b/admin/dashboard/templates/system_llm.html new file mode 100644 index 0000000..ffb5a26 --- /dev/null +++ b/admin/dashboard/templates/system_llm.html @@ -0,0 +1,292 @@ +{% extends "base.html" %} + +{% block title %}全局配置 - 机器人管理后台{% endblock %} + +{% block content %} +
+
+
+
LLM Workspace
+

全局配置

+

集中维护全局 LLM 后端,插件只引用后端名,不再分散配置密钥和地址。

+
+
+ 刷新 + 新增后端 + 保存配置 +
+
+ + +
+
+

全局 LLM 配置

+

用表单统一管理 `config.yaml` 中的 `llm.backends`,系统和插件共享同一份后端配置。

+
+
+ 配置文件:{% raw %}{{ configPath }}{% endraw %} + 后端数量:{% raw %}{{ llmForm.backends.length }}{% endraw %} +
+
+ + + + + + + + + + +
+ +
+
+ {% raw %}{{ backend.name || `后端 ${index + 1}` }}{% endraw %} +
+ 删除 +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+
+
+ + +
+
+{% endblock %} + +{% block scripts %} + + +{% endblock %} diff --git a/admin/dashboard/templates/system_status.html b/admin/dashboard/templates/system_status.html index f47088d..7202584 100644 --- a/admin/dashboard/templates/system_status.html +++ b/admin/dashboard/templates/system_status.html @@ -7,159 +7,28 @@
System Workspace
-

系统控制台

-

把监控和全局 LLM 配置集中到同一页,减少来回切换。

+

资源监控

+

直接在后台查看系统资源变化与运行状态,保持监控入口简洁清晰。

+ 刷新面板 + 新窗口打开 重启服务
- - - -
-
-

监控面板

-

直接在控制台内查看系统资源变化与运行状态。

-
-
-
{{ src_url }}
- 刷新 - 新窗口打开 -
-
-
- -
-
-
- - - -
-
-

全局 LLM 配置

-

用表单统一管理 `config.yaml` 中的 `llm.backends`,插件只引用后端名。

-
-
- 刷新 - 新增后端 - 保存配置 -
-
- -
- 配置文件:{% raw %}{{ configPath }}{% endraw %} - 后端数量:{% raw %}{{ llmForm.backends.length }}{% endraw %} -
- - - - - - - - - - -
- -
-
- {% raw %}{{ backend.name || `后端 ${index + 1}` }}{% endraw %} -
- 删除 -
- - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
- -
-
-
-
- - -
-
-
+ +
+
+

监控面板

+

直接在控制台内查看系统资源变化与运行状态。

+
+
{{ src_url }}
+
+
+ +
+
{% endblock %} @@ -171,26 +40,12 @@ data() { return { currentView: '14', - activeTab: 'monitor', frameUrl: '{{ src_url }}', - restarting: false, - configPath: '', - llmForm: { - default_backend: '', - backends: [] - } - } - }, - computed: { - backendNameOptions() { - return (this.llmForm.backends || []) - .map(item => item.name) - .filter(Boolean); + restarting: false } }, mounted() { this.currentView = '14'; - this.loadLlmConfig(); }, methods: { reloadIframe() { @@ -225,96 +80,6 @@ } finally { this.restarting = false; } - }, - newBackend() { - return { - uid: `${Date.now()}_${Math.random().toString(36).slice(2, 8)}`, - name: '', - provider: 'dify', - mode: '', - model: '', - api_base_url: '', - api_url: '', - endpoint: '', - api_key: '', - response_mode: '', - workflow_output_key: '', - timeout_seconds: 60, - request_timeout: 60, - temperature: 0.7, - max_tokens: 1024, - max_retries: 3, - retry_delay_seconds: 1.0, - stream: false - }; - }, - normalizeBackend(item) { - return { - uid: `${Date.now()}_${Math.random().toString(36).slice(2, 8)}`, - name: item.name || '', - provider: item.provider || 'dify', - mode: item.mode || '', - model: item.model || '', - api_base_url: item.api_base_url || '', - api_url: item.api_url || '', - endpoint: item.endpoint || '', - api_key: item.api_key || '', - response_mode: item.response_mode || '', - workflow_output_key: item.workflow_output_key || '', - timeout_seconds: item.timeout_seconds ?? 60, - request_timeout: item.request_timeout ?? 60, - temperature: item.temperature ?? 0.7, - max_tokens: item.max_tokens ?? 1024, - max_retries: item.max_retries ?? 3, - retry_delay_seconds: item.retry_delay_seconds ?? 1.0, - stream: !!item.stream - }; - }, - addBackend() { - this.llmForm.backends.push(this.newBackend()); - }, - removeBackend(index) { - const removed = this.llmForm.backends[index]; - this.llmForm.backends.splice(index, 1); - if (removed && removed.name && this.llmForm.default_backend === removed.name) { - this.llmForm.default_backend = ''; - } - }, - async loadLlmConfig() { - try { - const response = await axios.get('/api/system/llm_config'); - if (response.data.success) { - const data = response.data.data || {}; - this.configPath = data.config_path || ''; - this.llmForm.default_backend = data.default_backend || ''; - this.llmForm.backends = (data.backends || []).map(item => this.normalizeBackend(item)); - } else { - this.$message.error(response.data.message || '读取全局 LLM 配置失败'); - } - } catch (error) { - this.$message.error(error.response?.data?.message || '读取全局 LLM 配置失败'); - } - }, - async saveLlmConfig() { - const payload = { - default_backend: this.llmForm.default_backend || '', - backends: (this.llmForm.backends || []).map(item => { - const cleaned = { ...item }; - delete cleaned.uid; - return cleaned; - }) - }; - try { - const response = await axios.post('/api/system/llm_config', payload); - if (response.data.success) { - this.$message.success(response.data.message || '保存成功'); - this.loadLlmConfig(); - } else { - this.$message.error(response.data.message || '保存失败'); - } - } catch (error) { - this.$message.error(error.response?.data?.message || '保存失败'); - } } } }); @@ -331,7 +96,6 @@ .page-eyebrow { font-size: 12px; text-transform: uppercase; letter-spacing: .08em; color: #6366f1; font-weight: 700; margin-bottom: 8px; } .page-hero-copy h1 { font-size: 30px; line-height: 1.1; margin-bottom: 10px; color: #0f172a; } .page-hero-copy p { color: #64748b; font-size: 14px; } - .system-tabs { width: 100%; } .workspace-header { display: flex; align-items: center; justify-content: space-between; gap: 16px; } .workspace-header h3 { font-size: 18px; margin-bottom: 4px; } .workspace-header p { font-size: 13px; color: #64748b; } @@ -339,28 +103,15 @@ max-width: 40%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; color: #94a3b8; padding: 8px 12px; border-radius: 999px; background: rgba(248,250,252,0.9); border: 1px solid rgba(148,163,184,0.12); } - .iframe-shell-card { height: calc(100vh - 280px); } + .iframe-shell-card { height: calc(100vh - 230px); } .iframe-shell-card .el-card__body { height: calc(100% - 73px); } .iframe-shell { height: 100%; border-radius: 18px; overflow: hidden; border: 1px solid rgba(148,163,184,0.12); background: rgba(248,250,252,0.82); } .iframe-shell iframe { width: 100%; height: 100%; border: none; display: block; background: #fff; } - .workspace-card .el-card__body { display: flex; flex-direction: column; gap: 16px; } - .config-meta { display: flex; justify-content: space-between; gap: 12px; color: #64748b; font-size: 12px; } - .backend-list { display: flex; flex-direction: column; gap: 16px; } - .backend-card { border-radius: 18px; border: 1px solid rgba(148,163,184,0.16); } - .backend-card-header { display: flex; align-items: center; justify-content: space-between; gap: 12px; } - .backend-grid { - display: grid; - grid-template-columns: repeat(2, minmax(260px, 1fr)); - gap: 8px 16px; - } - .backend-switches { display: flex; align-items: center; gap: 16px; padding: 0 12px 8px; } - .danger-text { color: #dc2626; } @media (max-width: 960px) { .page-hero { flex-direction: column; align-items: flex-start; } .workspace-header { flex-direction: column; align-items: flex-start; } .page-hero-actions { flex-wrap: wrap; } .iframe-url { max-width: 100%; } - .backend-grid { grid-template-columns: 1fr; } } {% endblock %}