feat: 后台增加对话配置+工具配置管理
新增对话配置页面: - 配置普通对话使用的LLM、可用工具 - 历史记录数、Temperature、系统提示词 - 支持多配置、设为默认 搜索配置改为工具配置: - 工具类型: search/calculator/image/code/weather/custom - 搜索作为工具之一管理 - 支持添加多种工具 - 工具配置: ID、名称、类型、提供商、API、Key、额外配置 API新增: - /api/admin/chat - 对话配置CRUD - /api/admin/tools - 工具配置CRUD - /api/config 返回 tools 和 chat_config
This commit is contained in:
187
backend/app.py
187
backend/app.py
@@ -76,14 +76,17 @@ def init_db():
|
|||||||
)
|
)
|
||||||
''')
|
''')
|
||||||
|
|
||||||
# 搜索配置表
|
# 搜索配置表(改为工具配置)
|
||||||
cursor.execute('''
|
cursor.execute('''
|
||||||
CREATE TABLE IF NOT EXISTS search_configs (
|
CREATE TABLE IF NOT EXISTS tool_configs (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
tool_id TEXT NOT NULL UNIQUE,
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
|
type TEXT NOT NULL,
|
||||||
provider TEXT NOT NULL,
|
provider TEXT NOT NULL,
|
||||||
api_url TEXT NOT NULL,
|
api_url TEXT NOT NULL,
|
||||||
api_key TEXT NOT NULL,
|
api_key TEXT,
|
||||||
|
config_json TEXT,
|
||||||
max_results INTEGER DEFAULT 10,
|
max_results INTEGER DEFAULT 10,
|
||||||
is_default INTEGER DEFAULT 0,
|
is_default INTEGER DEFAULT 0,
|
||||||
is_active INTEGER DEFAULT 1,
|
is_active INTEGER DEFAULT 1,
|
||||||
@@ -92,6 +95,24 @@ def init_db():
|
|||||||
)
|
)
|
||||||
''')
|
''')
|
||||||
|
|
||||||
|
# 对话配置表
|
||||||
|
cursor.execute('''
|
||||||
|
CREATE TABLE IF NOT EXISTS chat_configs (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
config_id TEXT NOT NULL UNIQUE,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
llm_config_id INTEGER,
|
||||||
|
enable_tools TEXT,
|
||||||
|
max_history INTEGER DEFAULT 20,
|
||||||
|
temperature REAL DEFAULT 0.7,
|
||||||
|
system_prompt TEXT,
|
||||||
|
is_default INTEGER DEFAULT 0,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (llm_config_id) REFERENCES llm_configs(id)
|
||||||
|
)
|
||||||
|
''')
|
||||||
|
|
||||||
# 系统配置表
|
# 系统配置表
|
||||||
cursor.execute('''
|
cursor.execute('''
|
||||||
CREATE TABLE IF NOT EXISTS system_configs (
|
CREATE TABLE IF NOT EXISTS system_configs (
|
||||||
@@ -133,13 +154,21 @@ def init_db():
|
|||||||
'2259e33a1357460abe17919aaf81e73d.K44a8LPQTmFM5PKm', 'glm-4.5-air', 1)
|
'2259e33a1357460abe17919aaf81e73d.K44a8LPQTmFM5PKm', 'glm-4.5-air', 1)
|
||||||
''')
|
''')
|
||||||
|
|
||||||
# 初始化默认搜索配置
|
# 初始化默认工具配置(搜索工具)
|
||||||
cursor.execute('SELECT COUNT(*) FROM search_configs')
|
cursor.execute('SELECT COUNT(*) FROM tool_configs')
|
||||||
if cursor.fetchone()[0] == 0:
|
if cursor.fetchone()[0] == 0:
|
||||||
cursor.execute('''
|
cursor.execute('''
|
||||||
INSERT INTO search_configs (name, provider, api_url, api_key, is_default)
|
INSERT INTO tool_configs (tool_id, name, type, provider, api_url, api_key, max_results, is_default)
|
||||||
VALUES ('Tavily', 'tavily', 'https://api.tavily.com/search',
|
VALUES ('search', '联网搜索', 'search', 'tavily', 'https://api.tavily.com/search',
|
||||||
'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', 1)
|
'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', 10, 1)
|
||||||
|
''')
|
||||||
|
|
||||||
|
# 初始化默认对话配置
|
||||||
|
cursor.execute('SELECT COUNT(*) FROM chat_configs')
|
||||||
|
if cursor.fetchone()[0] == 0:
|
||||||
|
cursor.execute('''
|
||||||
|
INSERT INTO chat_configs (config_id, name, llm_config_id, enable_tools, is_default)
|
||||||
|
VALUES ('default', '默认对话配置', 1, 'search', 1)
|
||||||
''')
|
''')
|
||||||
|
|
||||||
# 初始化默认智能体
|
# 初始化默认智能体
|
||||||
@@ -391,69 +420,140 @@ def delete_agent(agent_id):
|
|||||||
return jsonify({'success': True})
|
return jsonify({'success': True})
|
||||||
|
|
||||||
|
|
||||||
# ==================== 搜索配置管理 ====================
|
# ==================== 对话配置管理 ====================
|
||||||
|
|
||||||
@app.route('/api/admin/search', methods=['GET'])
|
@app.route('/api/admin/chat', methods=['GET'])
|
||||||
def get_search_configs():
|
def get_chat_configs():
|
||||||
"""获取搜索配置"""
|
"""获取对话配置"""
|
||||||
conn = get_db()
|
conn = get_db()
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
cursor.execute('SELECT * FROM search_configs ORDER BY is_default DESC')
|
cursor.execute('SELECT * FROM chat_configs ORDER BY is_default DESC')
|
||||||
configs = [dict(row) for row in cursor.fetchall()]
|
configs = [dict(row) for row in cursor.fetchall()]
|
||||||
conn.close()
|
conn.close()
|
||||||
return jsonify(configs)
|
return jsonify(configs)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/admin/search', methods=['POST'])
|
@app.route('/api/admin/chat', methods=['POST'])
|
||||||
def add_search_config():
|
def add_chat_config():
|
||||||
"""添加搜索配置"""
|
"""添加对话配置"""
|
||||||
data = request.json
|
data = request.json
|
||||||
conn = get_db()
|
conn = get_db()
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
cursor.execute('''
|
cursor.execute('''
|
||||||
INSERT INTO search_configs (name, provider, api_url, api_key, max_results)
|
INSERT INTO chat_configs (config_id, name, llm_config_id, enable_tools, max_history, temperature, system_prompt)
|
||||||
VALUES (?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||||
''', (data['name'], data['provider'], data['api_url'], data['api_key'],
|
''', (data['config_id'], data['name'], data.get('llm_config_id'), data.get('enable_tools', ''),
|
||||||
|
data.get('max_history', 20), data.get('temperature', 0.7), data.get('system_prompt', '')))
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
return jsonify({'success': True})
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/admin/chat/<config_id>', methods=['PUT'])
|
||||||
|
def update_chat_config(config_id):
|
||||||
|
"""更新对话配置"""
|
||||||
|
data = request.json
|
||||||
|
conn = get_db()
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute('''
|
||||||
|
UPDATE chat_configs SET name=?, llm_config_id=?, enable_tools=?, max_history=?,
|
||||||
|
temperature=?, system_prompt=?, updated_at=CURRENT_TIMESTAMP WHERE config_id=?
|
||||||
|
''', (data['name'], data.get('llm_config_id'), data.get('enable_tools', ''),
|
||||||
|
data.get('max_history', 20), data.get('temperature', 0.7),
|
||||||
|
data.get('system_prompt', ''), config_id))
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
return jsonify({'success': True})
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/admin/chat/<config_id>', methods=['DELETE'])
|
||||||
|
def delete_chat_config(config_id):
|
||||||
|
"""删除对话配置"""
|
||||||
|
conn = get_db()
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute('DELETE FROM chat_configs WHERE config_id=?', (config_id,))
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
return jsonify({'success': True})
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/admin/chat/<config_id>/default', methods=['POST'])
|
||||||
|
def set_default_chat(config_id):
|
||||||
|
"""设置默认对话配置"""
|
||||||
|
conn = get_db()
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute('UPDATE chat_configs SET is_default=0')
|
||||||
|
cursor.execute('UPDATE chat_configs SET is_default=1 WHERE config_id=?', (config_id,))
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
return jsonify({'success': True})
|
||||||
|
|
||||||
|
|
||||||
|
# ==================== 工具配置管理 ====================
|
||||||
|
|
||||||
|
@app.route('/api/admin/tools', methods=['GET'])
|
||||||
|
def get_tool_configs():
|
||||||
|
"""获取工具配置"""
|
||||||
|
conn = get_db()
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute('SELECT * FROM tool_configs ORDER BY is_default DESC, type')
|
||||||
|
tools = [dict(row) for row in cursor.fetchall()]
|
||||||
|
conn.close()
|
||||||
|
return jsonify(tools)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/admin/tools', methods=['POST'])
|
||||||
|
def add_tool_config():
|
||||||
|
"""添加工具配置"""
|
||||||
|
data = request.json
|
||||||
|
conn = get_db()
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute('''
|
||||||
|
INSERT INTO tool_configs (tool_id, name, type, provider, api_url, api_key, config_json, max_results)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
|
''', (data['tool_id'], data['name'], data['type'], data['provider'],
|
||||||
|
data['api_url'], data.get('api_key', ''), data.get('config_json', ''),
|
||||||
data.get('max_results', 10)))
|
data.get('max_results', 10)))
|
||||||
conn.commit()
|
conn.commit()
|
||||||
conn.close()
|
conn.close()
|
||||||
return jsonify({'success': True})
|
return jsonify({'success': True})
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/admin/search/<int:id>', methods=['PUT'])
|
@app.route('/api/admin/tools/<tool_id>', methods=['PUT'])
|
||||||
def update_search_config(id):
|
def update_tool_config(tool_id):
|
||||||
"""更新搜索配置"""
|
"""更新工具配置"""
|
||||||
data = request.json
|
data = request.json
|
||||||
conn = get_db()
|
conn = get_db()
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
cursor.execute('''
|
cursor.execute('''
|
||||||
UPDATE search_configs SET name=?, provider=?, api_url=?, api_key=?, max_results=?,
|
UPDATE tool_configs SET name=?, type=?, provider=?, api_url=?, api_key=?,
|
||||||
updated_at=CURRENT_TIMESTAMP WHERE id=?
|
config_json=?, max_results=?, updated_at=CURRENT_TIMESTAMP WHERE tool_id=?
|
||||||
''', (data['name'], data['provider'], data['api_url'], data['api_key'],
|
''', (data['name'], data['type'], data['provider'], data['api_url'],
|
||||||
data.get('max_results', 10), id))
|
data.get('api_key', ''), data.get('config_json', ''),
|
||||||
|
data.get('max_results', 10), tool_id))
|
||||||
conn.commit()
|
conn.commit()
|
||||||
conn.close()
|
conn.close()
|
||||||
return jsonify({'success': True})
|
return jsonify({'success': True})
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/admin/search/<int:id>', methods=['DELETE'])
|
@app.route('/api/admin/tools/<tool_id>', methods=['DELETE'])
|
||||||
def delete_search_config(id):
|
def delete_tool_config(tool_id):
|
||||||
"""删除搜索配置"""
|
"""删除工具配置"""
|
||||||
conn = get_db()
|
conn = get_db()
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
cursor.execute('DELETE FROM search_configs WHERE id=?', (id,))
|
cursor.execute('DELETE FROM tool_configs WHERE tool_id=?', (tool_id,))
|
||||||
conn.commit()
|
conn.commit()
|
||||||
conn.close()
|
conn.close()
|
||||||
return jsonify({'success': True})
|
return jsonify({'success': True})
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/admin/search/<int:id>/default', methods=['POST'])
|
@app.route('/api/admin/tools/<tool_id>/default', methods=['POST'])
|
||||||
def set_default_search(id):
|
def set_default_tool(tool_id):
|
||||||
"""设置默认搜索"""
|
"""设置默认工具"""
|
||||||
conn = get_db()
|
conn = get_db()
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
cursor.execute('UPDATE search_configs SET is_default=0')
|
cursor.execute('UPDATE tool_configs SET is_default=0 WHERE type=(SELECT type FROM tool_configs WHERE tool_id=?)', (tool_id,))
|
||||||
cursor.execute('UPDATE search_configs SET is_default=1 WHERE id=?', (id,))
|
cursor.execute('UPDATE tool_configs SET is_default=1 WHERE tool_id=?', (tool_id,))
|
||||||
conn.commit()
|
conn.commit()
|
||||||
conn.close()
|
conn.close()
|
||||||
return jsonify({'success': True})
|
return jsonify({'success': True})
|
||||||
@@ -564,14 +664,18 @@ def get_frontend_config():
|
|||||||
cursor.execute('SELECT * FROM llm_configs WHERE is_default=1 AND is_active=1 LIMIT 1')
|
cursor.execute('SELECT * FROM llm_configs WHERE is_default=1 AND is_active=1 LIMIT 1')
|
||||||
llm = cursor.fetchone()
|
llm = cursor.fetchone()
|
||||||
|
|
||||||
# 获取默认搜索配置
|
# 获取默认工具配置(搜索等)
|
||||||
cursor.execute('SELECT * FROM search_configs WHERE is_default=1 AND is_active=1 LIMIT 1')
|
cursor.execute('SELECT * FROM tool_configs WHERE is_default=1 AND is_active=1')
|
||||||
search = cursor.fetchone()
|
tools = [dict(row) for row in cursor.fetchall()]
|
||||||
|
|
||||||
# 获取所有智能体
|
# 获取所有智能体
|
||||||
cursor.execute('SELECT agent_id, name, avatar, category, description, system_prompt, heat FROM agents WHERE is_active=1')
|
cursor.execute('SELECT agent_id, name, avatar, category, description, system_prompt, heat, enable_search FROM agents WHERE is_active=1')
|
||||||
agents = [dict(row) for row in cursor.fetchall()]
|
agents = [dict(row) for row in cursor.fetchall()]
|
||||||
|
|
||||||
|
# 获取默认对话配置
|
||||||
|
cursor.execute('SELECT * FROM chat_configs WHERE is_default=1 LIMIT 1')
|
||||||
|
chat_config = cursor.fetchone()
|
||||||
|
|
||||||
# 获取系统配置
|
# 获取系统配置
|
||||||
cursor.execute('SELECT key, value FROM system_configs')
|
cursor.execute('SELECT key, value FROM system_configs')
|
||||||
system = {row['key']: row['value'] for row in cursor.fetchall()}
|
system = {row['key']: row['value'] for row in cursor.fetchall()}
|
||||||
@@ -580,11 +684,12 @@ def get_frontend_config():
|
|||||||
|
|
||||||
config = {
|
config = {
|
||||||
'llm': dict(llm) if llm else None,
|
'llm': dict(llm) if llm else None,
|
||||||
'search': dict(search) if search else None,
|
'tools': tools,
|
||||||
'agents': agents,
|
'agents': agents,
|
||||||
|
'chat_config': dict(chat_config) if chat_config else None,
|
||||||
'system': {
|
'system': {
|
||||||
'appName': system.get('app_name', 'AI助手'),
|
'appName': system.get('app_name', 'AI助手'),
|
||||||
'version': system.get('app_version', '3.5.1'),
|
'version': system.get('app_version', '3.6.0'),
|
||||||
'enableSearch': system.get('enable_search', 'true') == 'true',
|
'enableSearch': system.get('enable_search', 'true') == 'true',
|
||||||
'guestLimits': {
|
'guestLimits': {
|
||||||
'chatSessions': int(system.get('guest_chat_sessions', '1')),
|
'chatSessions': int(system.get('guest_chat_sessions', '1')),
|
||||||
|
|||||||
@@ -440,9 +440,13 @@
|
|||||||
<span class="sidebar-icon">🤖</span>
|
<span class="sidebar-icon">🤖</span>
|
||||||
<span>智能体管理</span>
|
<span>智能体管理</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="sidebar-item" data-page="search">
|
<div class="sidebar-item" data-page="chat">
|
||||||
<span class="sidebar-icon">🔍</span>
|
<span class="sidebar-icon">💬</span>
|
||||||
<span>搜索配置</span>
|
<span>对话配置</span>
|
||||||
|
</div>
|
||||||
|
<div class="sidebar-item" data-page="tools">
|
||||||
|
<span class="sidebar-icon">🔧</span>
|
||||||
|
<span>工具配置</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="sidebar-item" data-page="system">
|
<div class="sidebar-item" data-page="system">
|
||||||
<span class="sidebar-icon">⚙️</span>
|
<span class="sidebar-icon">⚙️</span>
|
||||||
|
|||||||
409
www/admin.js
409
www/admin.js
@@ -5,7 +5,8 @@ const API_BASE = '';
|
|||||||
let currentPage = 'stats';
|
let currentPage = 'stats';
|
||||||
let llmConfigs = [];
|
let llmConfigs = [];
|
||||||
let agents = [];
|
let agents = [];
|
||||||
let searchConfigs = [];
|
let toolConfigs = [];
|
||||||
|
let chatConfigs = [];
|
||||||
let systemConfigs = {};
|
let systemConfigs = {};
|
||||||
|
|
||||||
// ==================== 登录 ====================
|
// ==================== 登录 ====================
|
||||||
@@ -86,8 +87,11 @@ async function loadPage(page) {
|
|||||||
case 'agents':
|
case 'agents':
|
||||||
await loadAgentsPage(content);
|
await loadAgentsPage(content);
|
||||||
break;
|
break;
|
||||||
case 'search':
|
case 'chat':
|
||||||
await loadSearchPage(content);
|
await loadChatConfigPage(content);
|
||||||
|
break;
|
||||||
|
case 'tools':
|
||||||
|
await loadToolsPage(content);
|
||||||
break;
|
break;
|
||||||
case 'system':
|
case 'system':
|
||||||
await loadSystemPage(content);
|
await loadSystemPage(content);
|
||||||
@@ -579,42 +583,46 @@ async function deleteAgent(agentId) {
|
|||||||
loadPage('agents');
|
loadPage('agents');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== 搜索配置页面 ====================
|
// ==================== 对话配置页面 ====================
|
||||||
|
|
||||||
async function loadSearchPage(content) {
|
async function loadChatConfigPage(content) {
|
||||||
searchConfigs = await fetchAPI('/api/admin/search');
|
chatConfigs = await fetchAPI('/api/admin/chat');
|
||||||
|
llmConfigs = await fetchAPI('/api/admin/llm');
|
||||||
|
toolConfigs = await fetchAPI('/api/admin/tools');
|
||||||
|
|
||||||
content.innerHTML = `
|
content.innerHTML = `
|
||||||
<div class="content-header">
|
<div class="content-header">
|
||||||
<h1 class="content-title">搜索配置</h1>
|
<h1 class="content-title">对话配置</h1>
|
||||||
<button class="add-btn" onclick="showAddSearchModal()">+ 添加配置</button>
|
<button class="add-btn" onclick="showAddChatConfigModal()">+ 添加配置</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="data-table">
|
<div class="data-table">
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>名称</th>
|
<th>配置名称</th>
|
||||||
<th>提供商</th>
|
<th>LLM</th>
|
||||||
<th>API URL</th>
|
<th>可用工具</th>
|
||||||
<th>最大结果数</th>
|
<th>历史记录数</th>
|
||||||
|
<th>Temperature</th>
|
||||||
<th>状态</th>
|
<th>状态</th>
|
||||||
<th>操作</th>
|
<th>操作</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
${searchConfigs.map(c => `
|
${chatConfigs.map(c => `
|
||||||
<tr>
|
<tr>
|
||||||
<td>${c.name} ${c.is_default ? '<span class="default-badge">默认</span>' : ''}</td>
|
<td>${c.name} ${c.is_default ? '<span class="default-badge">默认</span>' : ''}</td>
|
||||||
<td>${c.provider}</td>
|
<td>${llmConfigs.find(l => l.id === c.llm_config_id)?.name || '未指定'}</td>
|
||||||
<td style="max-width: 200px; overflow: hidden; text-overflow: ellipsis;">${c.api_url}</td>
|
<td>${c.enable_tools || '无'}</td>
|
||||||
<td>${c.max_results}</td>
|
<td>${c.max_history || 20}</td>
|
||||||
<td>${c.is_active ? '✅ 启用' : '❌ 禁用'}</td>
|
<td>${c.temperature || 0.7}</td>
|
||||||
|
<td>${c.is_default ? '✅ 默认' : '-'}</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="action-btns">
|
<div class="action-btns">
|
||||||
<button class="action-btn edit" onclick="showEditSearchModal(${c.id})">编辑</button>
|
<button class="action-btn edit" onclick="showEditChatConfigModal('${c.config_id}')">编辑</button>
|
||||||
${!c.is_default ? `<button class="action-btn default" onclick="setDefaultSearch(${c.id})">设为默认</button>` : ''}
|
${!c.is_default ? `<button class="action-btn default" onclick="setDefaultChatConfig('${c.config_id}')">设为默认</button>` : ''}
|
||||||
<button class="action-btn delete" onclick="deleteSearch(${c.id})">删除</button>
|
<button class="action-btn delete" onclick="deleteChatConfig('${c.config_id}')">删除</button>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -622,120 +630,365 @@ async function loadSearchPage(content) {
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div style="margin-top: 24px; padding: 16px; background: white; border-radius: 12px;">
|
||||||
|
<h3 style="margin-bottom: 16px;">对话配置说明</h3>
|
||||||
|
<p style="color: #718096; font-size: 14px;">
|
||||||
|
对话配置用于控制普通对话的各项参数。可以选择使用的大模型接口、启用的工具、历史记录数量等。
|
||||||
|
前端 APP 会自动使用默认配置进行对话。
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function showAddSearchModal() {
|
function showAddChatConfigModal() {
|
||||||
showModal('添加搜索配置', `
|
showModal('添加对话配置', `
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">名称</label>
|
<label class="form-label">配置ID</label>
|
||||||
<input type="text" class="form-input" id="searchName" placeholder="如:Tavily">
|
<input type="text" class="form-input" id="chatConfigId" placeholder="如:default">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">提供商</label>
|
<label class="form-label">配置名称</label>
|
||||||
<select class="form-select" id="searchProvider">
|
<input type="text" class="form-input" id="chatConfigName" placeholder="如:默认对话配置">
|
||||||
<option value="tavily">Tavily</option>
|
</div>
|
||||||
<option value="google">Google</option>
|
<div class="form-group">
|
||||||
<option value="bing">Bing</option>
|
<label class="form-label">LLM 配置</label>
|
||||||
<option value="custom">自定义</option>
|
<select class="form-select" id="chatLLMConfig">
|
||||||
|
${llmConfigs.map(l => `<option value="${l.id}">${l.name}</option>`).join('')}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">API URL</label>
|
<label class="form-label">可用工具(逗号分隔)</label>
|
||||||
<input type="text" class="form-input" id="searchApiUrl" placeholder="API endpoint URL">
|
<input type="text" class="form-input" id="chatEnableTools" placeholder="如:search,calculator">
|
||||||
|
<span style="color: #999; font-size: 12px;">可用工具: ${toolConfigs.map(t => t.tool_id).join(', ')}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">API Key</label>
|
<label class="form-label">历史记录数</label>
|
||||||
<input type="text" class="form-input" id="searchApiKey" placeholder="API Key">
|
<input type="number" class="form-input" id="chatMaxHistory" value="20">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">最大结果数</label>
|
<label class="form-label">Temperature</label>
|
||||||
<input type="number" class="form-input" id="searchMaxResults" value="10">
|
<input type="number" class="form-input" id="chatTemperature" value="0.7" step="0.1">
|
||||||
</div>
|
</div>
|
||||||
<button class="form-submit" onclick="saveSearch()">保存</button>
|
<div class="form-group">
|
||||||
|
<label class="form-label">系统提示词(可选)</label>
|
||||||
|
<textarea class="form-textarea" id="chatSystemPrompt" placeholder="全局系统提示词"></textarea>
|
||||||
|
</div>
|
||||||
|
<button class="form-submit" onclick="saveChatConfig()">保存</button>
|
||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function showEditSearchModal(id) {
|
function showEditChatConfigModal(configId) {
|
||||||
const config = searchConfigs.find(c => c.id === id);
|
const config = chatConfigs.find(c => c.config_id === configId);
|
||||||
if (!config) return;
|
if (!config) return;
|
||||||
|
|
||||||
showModal('编辑搜索配置', `
|
showModal('编辑对话配置', `
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">名称</label>
|
<label class="form-label">配置ID(不可修改)</label>
|
||||||
<input type="text" class="form-input" id="searchName" value="${config.name}">
|
<input type="text" class="form-input" id="chatConfigId" value="${config.config_id}" readonly style="background: #f5f7fa;">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">提供商</label>
|
<label class="form-label">配置名称</label>
|
||||||
<select class="form-select" id="searchProvider">
|
<input type="text" class="form-input" id="chatConfigName" value="${config.name}">
|
||||||
<option value="tavily" ${config.provider === 'tavily' ? 'selected' : ''}>Tavily</option>
|
</div>
|
||||||
<option value="google" ${config.provider === 'google' ? 'selected' : ''}>Google</option>
|
<div class="form-group">
|
||||||
<option value="bing" ${config.provider === 'bing' ? 'selected' : ''}>Bing</option>
|
<label class="form-label">LLM 配置</label>
|
||||||
<option value="custom" ${config.provider === 'custom' ? 'selected' : ''}>自定义</option>
|
<select class="form-select" id="chatLLMConfig">
|
||||||
|
${llmConfigs.map(l => `<option value="${l.id}" ${l.id === config.llm_config_id ? 'selected' : ''}>${l.name}</option>`).join('')}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">API URL</label>
|
<label class="form-label">可用工具(逗号分隔)</label>
|
||||||
<input type="text" class="form-input" id="searchApiUrl" value="${config.api_url}">
|
<input type="text" class="form-input" id="chatEnableTools" value="${config.enable_tools || ''}">
|
||||||
|
<span style="color: #999; font-size: 12px;">可用工具: ${toolConfigs.map(t => t.tool_id).join(', ')}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">API Key</label>
|
<label class="form-label">历史记录数</label>
|
||||||
<input type="text" class="form-input" id="searchApiKey" value="${config.api_key}">
|
<input type="number" class="form-input" id="chatMaxHistory" value="${config.max_history || 20}">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">最大结果数</label>
|
<label class="form-label">Temperature</label>
|
||||||
<input type="number" class="form-input" id="searchMaxResults" value="${config.max_results}">
|
<input type="number" class="form-input" id="chatTemperature" value="${config.temperature || 0.7}" step="0.1">
|
||||||
</div>
|
</div>
|
||||||
<button class="form-submit" onclick="updateSearch(${id})">保存</button>
|
<div class="form-group">
|
||||||
|
<label class="form-label">系统提示词(可选)</label>
|
||||||
|
<textarea class="form-textarea" id="chatSystemPrompt">${config.system_prompt || ''}</textarea>
|
||||||
|
</div>
|
||||||
|
<button class="form-submit" onclick="updateChatConfig('${configId}')">保存</button>
|
||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveSearch() {
|
async function saveChatConfig() {
|
||||||
const data = {
|
const data = {
|
||||||
name: document.getElementById('searchName').value,
|
config_id: document.getElementById('chatConfigId').value,
|
||||||
provider: document.getElementById('searchProvider').value,
|
name: document.getElementById('chatConfigName').value,
|
||||||
api_url: document.getElementById('searchApiUrl').value,
|
llm_config_id: parseInt(document.getElementById('chatLLMConfig').value),
|
||||||
api_key: document.getElementById('searchApiKey').value,
|
enable_tools: document.getElementById('chatEnableTools').value,
|
||||||
max_results: parseInt(document.getElementById('searchMaxResults').value)
|
max_history: parseInt(document.getElementById('chatMaxHistory').value),
|
||||||
|
temperature: parseFloat(document.getElementById('chatTemperature').value),
|
||||||
|
system_prompt: document.getElementById('chatSystemPrompt').value
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!data.name || !data.api_url || !data.api_key) {
|
if (!data.config_id || !data.name) {
|
||||||
showToast('请填写完整信息');
|
showToast('请填写完整信息');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await fetchAPI('/api/admin/search', 'POST', data);
|
await fetchAPI('/api/admin/chat', 'POST', data);
|
||||||
closeModal();
|
closeModal();
|
||||||
showToast('添加成功');
|
showToast('添加成功');
|
||||||
loadPage('search');
|
loadPage('chat');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateSearch(id) {
|
async function updateChatConfig(configId) {
|
||||||
const data = {
|
const data = {
|
||||||
name: document.getElementById('searchName').value,
|
name: document.getElementById('chatConfigName').value,
|
||||||
provider: document.getElementById('searchProvider').value,
|
llm_config_id: parseInt(document.getElementById('chatLLMConfig').value),
|
||||||
api_url: document.getElementById('searchApiUrl').value,
|
enable_tools: document.getElementById('chatEnableTools').value,
|
||||||
api_key: document.getElementById('searchApiKey').value,
|
max_history: parseInt(document.getElementById('chatMaxHistory').value),
|
||||||
max_results: parseInt(document.getElementById('searchMaxResults').value)
|
temperature: parseFloat(document.getElementById('chatTemperature').value),
|
||||||
|
system_prompt: document.getElementById('chatSystemPrompt').value
|
||||||
};
|
};
|
||||||
|
|
||||||
await fetchAPI(`/api/admin/search/${id}`, 'PUT', data);
|
await fetchAPI(`/api/admin/chat/${configId}`, 'PUT', data);
|
||||||
closeModal();
|
closeModal();
|
||||||
showToast('更新成功');
|
showToast('更新成功');
|
||||||
loadPage('search');
|
loadPage('chat');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setDefaultSearch(id) {
|
async function setDefaultChatConfig(configId) {
|
||||||
await fetchAPI(`/api/admin/search/${id}/default`, 'POST');
|
await fetchAPI(`/api/admin/chat/${configId}/default`, 'POST');
|
||||||
showToast('已设为默认');
|
showToast('已设为默认');
|
||||||
loadPage('search');
|
loadPage('chat');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deleteSearch(id) {
|
async function deleteChatConfig(configId) {
|
||||||
if (!confirm('确定删除此配置?')) return;
|
if (!confirm('确定删除此配置?')) return;
|
||||||
await fetchAPI(`/api/admin/search/${id}`, 'DELETE');
|
await fetchAPI(`/api/admin/chat/${configId}`, 'DELETE');
|
||||||
showToast('删除成功');
|
showToast('删除成功');
|
||||||
loadPage('search');
|
loadPage('chat');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 工具配置页面 ====================
|
||||||
|
|
||||||
|
async function loadToolsPage(content) {
|
||||||
|
toolConfigs = await fetchAPI('/api/admin/tools');
|
||||||
|
|
||||||
|
const toolTypes = {
|
||||||
|
search: '搜索工具',
|
||||||
|
calculator: '计算器',
|
||||||
|
image: '图像生成',
|
||||||
|
code: '代码执行',
|
||||||
|
weather: '天气查询',
|
||||||
|
custom: '自定义'
|
||||||
|
};
|
||||||
|
|
||||||
|
content.innerHTML = `
|
||||||
|
<div class="content-header">
|
||||||
|
<h1 class="content-title">工具配置</h1>
|
||||||
|
<button class="add-btn" onclick="showAddToolModal()">+ 添加工具</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="data-table">
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>工具ID</th>
|
||||||
|
<th>名称</th>
|
||||||
|
<th>类型</th>
|
||||||
|
<th>提供商</th>
|
||||||
|
<th>API URL</th>
|
||||||
|
<th>状态</th>
|
||||||
|
<th>操作</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
${toolConfigs.map(t => `
|
||||||
|
<tr>
|
||||||
|
<td>${t.tool_id} ${t.is_default ? '<span class="default-badge">默认</span>' : ''}</td>
|
||||||
|
<td>${t.name}</td>
|
||||||
|
<td>${toolTypes[t.type] || t.type}</td>
|
||||||
|
<td>${t.provider}</td>
|
||||||
|
<td style="max-width: 200px; overflow: hidden; text-overflow: ellipsis;">${t.api_url}</td>
|
||||||
|
<td>${t.is_active ? '✅ 启用' : '❌ 禁用'}</td>
|
||||||
|
<td>
|
||||||
|
<div class="action-btns">
|
||||||
|
<button class="action-btn edit" onclick="showEditToolModal('${t.tool_id}')">编辑</button>
|
||||||
|
${!t.is_default ? `<button class="action-btn default" onclick="setDefaultTool('${t.tool_id}')">设为默认</button>` : ''}
|
||||||
|
<button class="action-btn delete" onclick="deleteTool('${t.tool_id}')">删除</button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
`).join('')}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="margin-top: 24px; padding: 16px; background: white; border-radius: 12px;">
|
||||||
|
<h3 style="margin-bottom: 16px;">工具类型说明</h3>
|
||||||
|
<div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px;">
|
||||||
|
<div style="padding: 12px; background: #f5f7fa; border-radius: 8px;">
|
||||||
|
<strong>🔍 search</strong>
|
||||||
|
<p style="color: #718096; font-size: 12px; margin-top: 4px;">联网搜索工具(Tavily、Google等)</p>
|
||||||
|
</div>
|
||||||
|
<div style="padding: 12px; background: #f5f7fa; border-radius: 8px;">
|
||||||
|
<strong>🧮 calculator</strong>
|
||||||
|
<p style="color: #718096; font-size: 12px; margin-top: 4px;">数学计算工具</p>
|
||||||
|
</div>
|
||||||
|
<div style="padding: 12px; background: #f5f7fa; border-radius: 8px;">
|
||||||
|
<strong>🎨 image</strong>
|
||||||
|
<p style="color: #718096; font-size: 12px; margin-top: 4px;">图像生成工具(DALL-E等)</p>
|
||||||
|
</div>
|
||||||
|
<div style="padding: 12px; background: #f5f7fa; border-radius: 8px;">
|
||||||
|
<strong>💻 code</strong>
|
||||||
|
<p style="color: #718096; font-size: 12px; margin-top: 4px;">代码执行工具</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function showAddToolModal() {
|
||||||
|
showModal('添加工具', `
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">工具ID</label>
|
||||||
|
<input type="text" class="form-input" id="toolId" placeholder="如:search">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">名称</label>
|
||||||
|
<input type="text" class="form-input" id="toolName" placeholder="如:联网搜索">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">类型</label>
|
||||||
|
<select class="form-select" id="toolType">
|
||||||
|
<option value="search">搜索工具</option>
|
||||||
|
<option value="calculator">计算器</option>
|
||||||
|
<option value="image">图像生成</option>
|
||||||
|
<option value="code">代码执行</option>
|
||||||
|
<option value="weather">天气查询</option>
|
||||||
|
<option value="custom">自定义</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">提供商</label>
|
||||||
|
<input type="text" class="form-input" id="toolProvider" placeholder="如:tavily">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">API URL</label>
|
||||||
|
<input type="text" class="form-input" id="toolApiUrl" placeholder="API endpoint URL">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">API Key</label>
|
||||||
|
<input type="text" class="form-input" id="toolApiKey" placeholder="API Key(可选)">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">最大结果数</label>
|
||||||
|
<input type="number" class="form-input" id="toolMaxResults" value="10">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">额外配置(JSON格式)</label>
|
||||||
|
<textarea class="form-textarea" id="toolConfigJson" placeholder="{}"></textarea>
|
||||||
|
</div>
|
||||||
|
<button class="form-submit" onclick="saveTool()">保存</button>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function showEditToolModal(toolId) {
|
||||||
|
const tool = toolConfigs.find(t => t.tool_id === toolId);
|
||||||
|
if (!tool) return;
|
||||||
|
|
||||||
|
const toolTypes = ['search', 'calculator', 'image', 'code', 'weather', 'custom'];
|
||||||
|
|
||||||
|
showModal('编辑工具', `
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">工具ID(不可修改)</label>
|
||||||
|
<input type="text" class="form-input" id="toolId" value="${tool.tool_id}" readonly style="background: #f5f7fa;">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">名称</label>
|
||||||
|
<input type="text" class="form-input" id="toolName" value="${tool.name}">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">类型</label>
|
||||||
|
<select class="form-select" id="toolType">
|
||||||
|
${toolTypes.map(t => `<option value="${t}" ${t === tool.type ? 'selected' : ''}>${t}</option>`).join('')}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">提供商</label>
|
||||||
|
<input type="text" class="form-input" id="toolProvider" value="${tool.provider}">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">API URL</label>
|
||||||
|
<input type="text" class="form-input" id="toolApiUrl" value="${tool.api_url}">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">API Key</label>
|
||||||
|
<input type="text" class="form-input" id="toolApiKey" value="${tool.api_key || ''}">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">最大结果数</label>
|
||||||
|
<input type="number" class="form-input" id="toolMaxResults" value="${tool.max_results || 10}">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">额外配置(JSON格式)</label>
|
||||||
|
<textarea class="form-textarea" id="toolConfigJson">${tool.config_json || '{}'}</textarea>
|
||||||
|
</div>
|
||||||
|
<button class="form-submit" onclick="updateTool('${toolId}')">保存</button>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveTool() {
|
||||||
|
const data = {
|
||||||
|
tool_id: document.getElementById('toolId').value,
|
||||||
|
name: document.getElementById('toolName').value,
|
||||||
|
type: document.getElementById('toolType').value,
|
||||||
|
provider: document.getElementById('toolProvider').value,
|
||||||
|
api_url: document.getElementById('toolApiUrl').value,
|
||||||
|
api_key: document.getElementById('toolApiKey').value,
|
||||||
|
max_results: parseInt(document.getElementById('toolMaxResults').value),
|
||||||
|
config_json: document.getElementById('toolConfigJson').value
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!data.tool_id || !data.name || !data.type) {
|
||||||
|
showToast('请填写完整信息');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await fetchAPI('/api/admin/tools', 'POST', data);
|
||||||
|
closeModal();
|
||||||
|
showToast('添加成功');
|
||||||
|
loadPage('tools');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateTool(toolId) {
|
||||||
|
const data = {
|
||||||
|
name: document.getElementById('toolName').value,
|
||||||
|
type: document.getElementById('toolType').value,
|
||||||
|
provider: document.getElementById('toolProvider').value,
|
||||||
|
api_url: document.getElementById('toolApiUrl').value,
|
||||||
|
api_key: document.getElementById('toolApiKey').value,
|
||||||
|
max_results: parseInt(document.getElementById('toolMaxResults').value),
|
||||||
|
config_json: document.getElementById('toolConfigJson').value
|
||||||
|
};
|
||||||
|
|
||||||
|
await fetchAPI(`/api/admin/tools/${toolId}`, 'PUT', data);
|
||||||
|
closeModal();
|
||||||
|
showToast('更新成功');
|
||||||
|
loadPage('tools');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setDefaultTool(toolId) {
|
||||||
|
await fetchAPI(`/api/admin/tools/${toolId}/default`, 'POST');
|
||||||
|
showToast('已设为默认');
|
||||||
|
loadPage('tools');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteTool(toolId) {
|
||||||
|
if (!confirm('确定删除此工具?')) return;
|
||||||
|
await fetchAPI(`/api/admin/tools/${toolId}`, 'DELETE');
|
||||||
|
showToast('删除成功');
|
||||||
|
loadPage('tools');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== 系统设置页面 ====================
|
// ==================== 系统设置页面 ====================
|
||||||
|
|||||||
Reference in New Issue
Block a user