diff --git a/backend/app.py b/backend/app.py index ffc2743..08d09a1 100644 --- a/backend/app.py +++ b/backend/app.py @@ -54,6 +54,8 @@ def init_db(): model TEXT NOT NULL, max_tokens INTEGER DEFAULT 2048, temperature REAL DEFAULT 0.7, + enable_thinking INTEGER DEFAULT 0, + enable_vision INTEGER DEFAULT 0, is_default INTEGER DEFAULT 0, is_active INTEGER DEFAULT 1, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, @@ -221,6 +223,14 @@ def init_db(): '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', 10, 1) ''') + # 检测并添加 llm_configs 新字段(兼容旧数据库) + cursor.execute("PRAGMA table_info(llm_configs)") + llm_columns = [col[1] for col in cursor.fetchall()] + if 'enable_thinking' not in llm_columns: + cursor.execute("ALTER TABLE llm_configs ADD COLUMN enable_thinking INTEGER DEFAULT 0") + if 'enable_vision' not in llm_columns: + cursor.execute("ALTER TABLE llm_configs ADD COLUMN enable_vision INTEGER DEFAULT 0") + # 初始化默认对话配置 cursor.execute('SELECT COUNT(*) FROM chat_configs') if cursor.fetchone()[0] == 0: @@ -1005,10 +1015,11 @@ def add_llm_config(): conn = get_db() cursor = conn.cursor() cursor.execute(''' - INSERT INTO llm_configs (name, provider, api_url, api_key, model, max_tokens, temperature) - VALUES (?, ?, ?, ?, ?, ?, ?) + INSERT INTO llm_configs (name, provider, api_url, api_key, model, max_tokens, temperature, enable_thinking, enable_vision) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) ''', (data['name'], data['provider'], data['api_url'], data['api_key'], - data['model'], data.get('max_tokens', 2048), data.get('temperature', 0.7))) + data['model'], data.get('max_tokens', 2048), data.get('temperature', 0.7), + data.get('enable_thinking', 0), data.get('enable_vision', 0))) conn.commit() config_id = cursor.lastrowid conn.close() @@ -1023,9 +1034,10 @@ def update_llm_config(id): cursor = conn.cursor() cursor.execute(''' UPDATE llm_configs SET name=?, provider=?, api_url=?, api_key=?, model=?, - max_tokens=?, temperature=?, updated_at=CURRENT_TIMESTAMP WHERE id=? + max_tokens=?, temperature=?, enable_thinking=?, enable_vision=?, updated_at=CURRENT_TIMESTAMP WHERE id=? ''', (data['name'], data['provider'], data['api_url'], data['api_key'], - data['model'], data.get('max_tokens', 2048), data.get('temperature', 0.7), id)) + data['model'], data.get('max_tokens', 2048), data.get('temperature', 0.7), + data.get('enable_thinking', 0), data.get('enable_vision', 0), id)) conn.commit() conn.close() return jsonify({'success': True}) diff --git a/www/admin.html b/www/admin.html index 7e02108..3eb8366 100644 --- a/www/admin.html +++ b/www/admin.html @@ -401,6 +401,54 @@ .toast.show { opacity: 1; } + + /* 表单行(双列布局) */ + .form-row { + display: flex; + gap: 20px; + margin-bottom: 16px; + } + + .form-group-half { + flex: 1; + } + + /* 表单提示 */ + .form-tip { + font-size: 12px; + color: #999; + margin-top: 4px; + } + + /* 开关按钮 */ + .toggle-switch { + width: 50px; + height: 26px; + background: #ccc; + border-radius: 13px; + cursor: pointer; + position: relative; + transition: background 0.3s; + } + + .toggle-switch.active { + background: var(--primary); + } + + .toggle-slider { + width: 22px; + height: 22px; + background: white; + border-radius: 50%; + position: absolute; + top: 2px; + left: 2px; + transition: left 0.3s; + } + + .toggle-switch.active .toggle-slider { + left: 26px; + } diff --git a/www/admin.js b/www/admin.js index 63c4ee0..a148c03 100644 --- a/www/admin.js +++ b/www/admin.js @@ -559,7 +559,8 @@ async function loadLLMPage(content) { 名称 提供商 模型 - API URL + 思考 + 视觉 状态 操作 @@ -570,7 +571,8 @@ async function loadLLMPage(content) { ${c.name} ${c.is_default ? '默认' : ''} ${c.provider} ${c.model} - ${c.api_url} + ${c.enable_thinking ? '🧠 支持' : '—'} + ${c.enable_vision ? '👁️ 支持' : '—'} ${c.is_active ? '✅ 启用' : '❌ 禁用'}
@@ -622,8 +624,28 @@ function showAddLLMModal() {
+
+
+ +
+
+
+
支持深度思考/推理能力
+
+
+ +
+
+
+
支持图片输入/多模态
+
+
`); + + // 绑定开关事件 + bindToggleSwitch('llmThinkingToggle'); + bindToggleSwitch('llmVisionToggle'); } function showEditLLMModal(id) { @@ -664,8 +686,28 @@ function showEditLLMModal(id) { +
+
+ +
+
+
+
支持深度思考/推理能力
+
+
+ +
+
+
+
支持图片输入/多模态
+
+
`); + + // 绑定开关事件 + bindToggleSwitch('llmThinkingToggle'); + bindToggleSwitch('llmVisionToggle'); } async function saveLLM() { @@ -676,7 +718,9 @@ async function saveLLM() { api_key: document.getElementById('llmApiKey').value, model: document.getElementById('llmModel').value, max_tokens: parseInt(document.getElementById('llmMaxTokens').value), - temperature: parseFloat(document.getElementById('llmTemperature').value) + temperature: parseFloat(document.getElementById('llmTemperature').value), + enable_thinking: parseInt(document.getElementById('llmThinkingToggle')?.dataset.value || 0), + enable_vision: parseInt(document.getElementById('llmVisionToggle')?.dataset.value || 0) }; if (!data.name || !data.api_url || !data.api_key || !data.model) { @@ -698,7 +742,9 @@ async function updateLLM(id) { api_key: document.getElementById('llmApiKey').value, model: document.getElementById('llmModel').value, max_tokens: parseInt(document.getElementById('llmMaxTokens').value), - temperature: parseFloat(document.getElementById('llmTemperature').value) + temperature: parseFloat(document.getElementById('llmTemperature').value), + enable_thinking: parseInt(document.getElementById('llmThinkingToggle')?.dataset.value || 0), + enable_vision: parseInt(document.getElementById('llmVisionToggle')?.dataset.value || 0) }; await fetchAPI(`/api/admin/llm/${id}`, 'PUT', data); @@ -1512,6 +1558,18 @@ function closeModal() { document.getElementById('modal').classList.remove('show'); } +function bindToggleSwitch(id) { + const toggle = document.getElementById(id); + if (!toggle) return; + + toggle.addEventListener('click', () => { + const current = parseInt(toggle.dataset.value) || 0; + const newValue = current === 0 ? 1 : 0; + toggle.dataset.value = newValue; + toggle.classList.toggle('active', newValue === 1); + }); +} + function showToast(message) { const toast = document.getElementById('toast'); toast.textContent = message;