feat(webui): settings 页面可编辑工具调用限制与超时时间
- settings.html: 新增「工具调用限制」表单(5个字段) - settings.html: 新增「消息超时」输入框 - server.py: _handle_set_settings 修复空 api_config 时不清空 API key - 前后端 format:api_config 和 tool_limits 分开提交,互不影响
This commit is contained in:
@ -462,16 +462,16 @@ class BackendServer:
|
|||||||
api_config = body.get("api_config", {})
|
api_config = body.get("api_config", {})
|
||||||
tool_limits = body.get("tool_limits", {})
|
tool_limits = body.get("tool_limits", {})
|
||||||
|
|
||||||
api_key = api_config.get("api_key", "")
|
# 仅当 api_config 有值时更新 API 配置(避免单独保存 tool_limits 时清空 API key)
|
||||||
base_url = api_config.get("base_url", "https://api.deepseek.com")
|
if api_config:
|
||||||
model = api_config.get("model", "deepseek-v4-flash")
|
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"))
|
||||||
self.update_config(api_key, base_url, model)
|
model = api_config.get("model", self._config.get("model", "deepseek-v4-flash"))
|
||||||
|
self.update_config(api_key, base_url, model)
|
||||||
# 通用配置字段(如 message_timeout)
|
# 通用配置字段(如 message_timeout)
|
||||||
for key in ["message_timeout"]:
|
for key in ["message_timeout"]:
|
||||||
if key in api_config:
|
if key in api_config:
|
||||||
self._config[key] = int(api_config[key])
|
self._config[key] = int(api_config[key])
|
||||||
|
|
||||||
limits_keys = [
|
limits_keys = [
|
||||||
"persona_update_max",
|
"persona_update_max",
|
||||||
|
|||||||
@ -204,10 +204,42 @@
|
|||||||
<label for="settings_model">Model</label>
|
<label for="settings_model">Model</label>
|
||||||
<input type="text" id="settings_model" placeholder="deepseek-chat">
|
<input type="text" id="settings_model" placeholder="deepseek-chat">
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" class="btn" id="saveApiConfigBtn">保 存 API 配 置</button>
|
<div class="form-group">
|
||||||
|
<label for="settings_timeout">消息超时(秒)</label>
|
||||||
|
<input type="number" id="settings_timeout" placeholder="1200" min="60" step="60">
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn" id="saveApiConfigBtn">保 存 配 置</button>
|
||||||
</form>
|
</form>
|
||||||
<div id="apiConfigStatus" style="text-align:center;font-size:12px;color:#666688;margin-top:5px;"></div>
|
<div id="apiConfigStatus" style="text-align:center;font-size:12px;color:#666688;margin-top:5px;"></div>
|
||||||
|
|
||||||
|
<!-- 工具调用限制 -->
|
||||||
|
<div class="section-title"><i class="fas fa-sliders-h"></i> 工具调用限制</div>
|
||||||
|
<div class="limits-hint" style="color:#7777aa;font-size:12px;margin-bottom:10px;">AI 每轮对话中各类工具的最大调用次数</div>
|
||||||
|
<form id="toolLimitsForm">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="settings_persona_update">人设更新 <span style="color:#7777aa;font-size:12px;">(persona_update_max)</span></label>
|
||||||
|
<input type="number" id="settings_persona_update" min="0" max="100">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="settings_task_update">任务写入 <span style="color:#7777aa;font-size:12px;">(task_update_max)</span></label>
|
||||||
|
<input type="number" id="settings_task_update" min="1" max="200">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="settings_task_query">任务查询 <span style="color:#7777aa;font-size:12px;">(task_query_max)</span></label>
|
||||||
|
<input type="number" id="settings_task_query" min="1" max="200">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="settings_memory_query">记忆查询 <span style="color:#7777aa;font-size:12px;">(memory_query_max)</span></label>
|
||||||
|
<input type="number" id="settings_memory_query" min="1" max="200">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="settings_memory_update">记忆写入 <span style="color:#7777aa;font-size:12px;">(memory_update_max)</span></label>
|
||||||
|
<input type="number" id="settings_memory_update" min="1" max="200">
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn" id="saveLimitsBtn">保 存 限 制</button>
|
||||||
|
</form>
|
||||||
|
<div id="limitsStatus" style="text-align:center;font-size:12px;color:#666688;margin-top:5px;"></div>
|
||||||
|
|
||||||
<!-- 修改密码 -->
|
<!-- 修改密码 -->
|
||||||
<div class="section-title"><i class="fas fa-key"></i> 修改密码</div>
|
<div class="section-title"><i class="fas fa-key"></i> 修改密码</div>
|
||||||
<form id="passwordForm">
|
<form id="passwordForm">
|
||||||
@ -477,9 +509,18 @@
|
|||||||
const data = await resp.json();
|
const data = await resp.json();
|
||||||
if (data.success && data.data) {
|
if (data.success && data.data) {
|
||||||
const cfg = data.data.api_config || {};
|
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_api_key').value = cfg.api_key || '';
|
||||||
document.getElementById('settings_base_url').value = cfg.base_url || 'https://api.deepseek.com';
|
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_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 = '✅ 配置已加载';
|
document.getElementById('apiConfigStatus').textContent = '✅ 配置已加载';
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -487,6 +528,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 保存 API 配置(含超时)
|
||||||
document.getElementById('apiConfigForm').addEventListener('submit', async (e) => {
|
document.getElementById('apiConfigForm').addEventListener('submit', async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const btn = document.getElementById('saveApiConfigBtn');
|
const btn = document.getElementById('saveApiConfigBtn');
|
||||||
@ -503,7 +545,8 @@
|
|||||||
api_config: {
|
api_config: {
|
||||||
api_key: document.getElementById('settings_api_key').value,
|
api_key: document.getElementById('settings_api_key').value,
|
||||||
base_url: document.getElementById('settings_base_url').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 = '⚠️ 网络错误';
|
statusEl.textContent = '⚠️ 网络错误';
|
||||||
} finally {
|
} finally {
|
||||||
btn.disabled = false;
|
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 = '保 存 限 制';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user