From 12ab1df7f5e6eb864359c1e52126efe5f9992500 Mon Sep 17 00:00:00 2001 From: root Date: Tue, 28 Apr 2026 20:39:26 +0800 Subject: [PATCH] =?UTF-8?q?feat(webui):=20settings=20=E9=A1=B5=E9=9D=A2?= =?UTF-8?q?=E5=8F=AF=E7=BC=96=E8=BE=91=E5=B7=A5=E5=85=B7=E8=B0=83=E7=94=A8?= =?UTF-8?q?=E9=99=90=E5=88=B6=E4=B8=8E=E8=B6=85=E6=97=B6=E6=97=B6=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - settings.html: 新增「工具调用限制」表单(5个字段) - settings.html: 新增「消息超时」输入框 - server.py: _handle_set_settings 修复空 api_config 时不清空 API key - 前后端 format:api_config 和 tool_limits 分开提交,互不影响 --- core/server.py | 20 ++++----- ui/templates/settings.html | 87 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 94 insertions(+), 13 deletions(-) diff --git a/core/server.py b/core/server.py index 0c84d7c..1350b64 100644 --- a/core/server.py +++ b/core/server.py @@ -462,16 +462,16 @@ class BackendServer: api_config = body.get("api_config", {}) tool_limits = body.get("tool_limits", {}) - api_key = api_config.get("api_key", "") - base_url = api_config.get("base_url", "https://api.deepseek.com") - model = api_config.get("model", "deepseek-v4-flash") - - self.update_config(api_key, base_url, model) - - # 通用配置字段(如 message_timeout) - for key in ["message_timeout"]: - if key in api_config: - self._config[key] = int(api_config[key]) + # 仅当 api_config 有值时更新 API 配置(避免单独保存 tool_limits 时清空 API key) + if api_config: + api_key = api_config.get("api_key", self._config.get("api_key", "")) + base_url = api_config.get("base_url", self._config.get("base_url", "https://api.deepseek.com")) + model = api_config.get("model", self._config.get("model", "deepseek-v4-flash")) + self.update_config(api_key, base_url, model) + # 通用配置字段(如 message_timeout) + for key in ["message_timeout"]: + if key in api_config: + self._config[key] = int(api_config[key]) limits_keys = [ "persona_update_max", diff --git a/ui/templates/settings.html b/ui/templates/settings.html index 5bee0e8..3dcdb5e 100644 --- a/ui/templates/settings.html +++ b/ui/templates/settings.html @@ -204,10 +204,42 @@ - +
+ + +
+
+ +
工具调用限制
+
AI 每轮对话中各类工具的最大调用次数
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+
+
修改密码
@@ -477,9 +509,18 @@ const data = await resp.json(); if (data.success && data.data) { const cfg = data.data.api_config || {}; + const limits = data.data.tool_limits || {}; + // API 配置 document.getElementById('settings_api_key').value = cfg.api_key || ''; document.getElementById('settings_base_url').value = cfg.base_url || 'https://api.deepseek.com'; document.getElementById('settings_model').value = cfg.model || 'deepseek-chat'; + document.getElementById('settings_timeout').value = cfg.message_timeout || 1200; + // 工具限制 + document.getElementById('settings_persona_update').value = limits.persona_update_max || 1; + document.getElementById('settings_task_update').value = limits.task_update_max || 20; + document.getElementById('settings_task_query').value = limits.task_query_max || 30; + document.getElementById('settings_memory_query').value = limits.memory_query_max || 30; + document.getElementById('settings_memory_update').value = limits.memory_update_max || 15; document.getElementById('apiConfigStatus').textContent = '✅ 配置已加载'; } } catch (e) { @@ -487,6 +528,7 @@ } } + // 保存 API 配置(含超时) document.getElementById('apiConfigForm').addEventListener('submit', async (e) => { e.preventDefault(); const btn = document.getElementById('saveApiConfigBtn'); @@ -503,7 +545,8 @@ api_config: { api_key: document.getElementById('settings_api_key').value, base_url: document.getElementById('settings_base_url').value, - model: document.getElementById('settings_model').value + model: document.getElementById('settings_model').value, + message_timeout: parseInt(document.getElementById('settings_timeout').value) || 1200 } }) }); @@ -518,7 +561,45 @@ statusEl.textContent = '⚠️ 网络错误'; } finally { btn.disabled = false; - btn.textContent = '保 存 API 配 置'; + btn.textContent = '保 存 配 置'; + } + }); + + // 保存工具调用限制 + document.getElementById('toolLimitsForm').addEventListener('submit', async (e) => { + e.preventDefault(); + const btn = document.getElementById('saveLimitsBtn'); + btn.disabled = true; + btn.textContent = '保存中...'; + const statusEl = document.getElementById('limitsStatus'); + statusEl.textContent = '🔄 保存中...'; + + try { + const resp = await fetch('/api/settings', { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + tool_limits: { + persona_update_max: parseInt(document.getElementById('settings_persona_update').value) || 1, + task_update_max: parseInt(document.getElementById('settings_task_update').value) || 20, + task_query_max: parseInt(document.getElementById('settings_task_query').value) || 30, + memory_query_max: parseInt(document.getElementById('settings_memory_query').value) || 30, + memory_update_max: parseInt(document.getElementById('settings_memory_update').value) || 15 + } + }) + }); + const data = await resp.json(); + if (data.success) { + statusEl.textContent = '✅ 工具限制已保存并生效'; + setTimeout(() => { statusEl.textContent = ''; }, 3000); + } else { + statusEl.textContent = '⚠️ 保存失败: ' + (data.error || '未知错误'); + } + } catch (e) { + statusEl.textContent = '⚠️ 网络错误'; + } finally { + btn.disabled = false; + btn.textContent = '保 存 限 制'; } });