const API_BASE = '/api/ai-config'; let currentConfig = null; let selectedProvider = 'openai'; document.addEventListener('DOMContentLoaded', function() { loadProviders(); loadConfig(); initEventListeners(); }); function initEventListeners() { document.getElementById('api-provider').addEventListener('change', function() { selectedProvider = this.value; updateProviderModels(); }); document.getElementById('temperature').addEventListener('input', function() { document.getElementById('temp-value').textContent = this.value; }); } async function loadProviders() { try { const response = await fetch(`${API_BASE}/providers`); const data = await response.json(); if (data.success) { renderProviders(data.data); } } catch (error) { console.error('加载提供商失败:', error); renderProviders(getDefaultProviders()); } } function getDefaultProviders() { return [ { id: 'openai', name: 'OpenAI', icon: 'fas fa-brain' }, { id: 'anthropic', name: 'Claude', icon: 'fas fa-robot' }, { id: 'google', name: 'Gemini', icon: 'fas fa-gem' }, { id: 'aliyun', name: '通义千问', icon: 'fas fa-cloud' }, { id: 'baidu', name: '文心一言', icon: 'fas fa-comments' }, { id: 'zhipu', name: '智谱清言', icon: 'fas fa-lightbulb' } ]; } function renderProviders(providers) { const grid = document.getElementById('provider-grid'); const iconMap = { 'openai': 'fas fa-brain', 'anthropic': 'fas fa-robot', 'google': 'fas fa-gem', 'aliyun': 'fas fa-cloud', 'baidu': 'fas fa-comments', 'zhipu': 'fas fa-lightbulb', 'custom': 'fas fa-cog' }; grid.innerHTML = providers.map(p => `
${p.name}
`).join(''); grid.querySelectorAll('.provider-card').forEach(card => { card.addEventListener('click', function() { grid.querySelectorAll('.provider-card').forEach(c => c.classList.remove('active')); this.classList.add('active'); selectedProvider = this.dataset.provider; document.getElementById('api-provider').value = selectedProvider; updateProviderModels(); }); }); } function updateProviderModels() { const modelSelect = document.getElementById('model-id'); const modelMap = { 'openai': ['gpt-4o', 'gpt-4-turbo', 'gpt-3.5-turbo'], 'anthropic': ['claude-3-opus', 'claude-3-sonnet', 'claude-3-haiku'], 'google': ['gemini-pro', 'gemini-pro-vision'], 'aliyun': ['qwen-max', 'qwen-plus', 'qwen-turbo'], 'baidu': ['ernie-4.0', 'ernie-3.5', 'ernie-speed'], 'zhipu': ['glm-4', 'glm-3-turbo'], 'custom': ['custom-model'] }; const apiBaseMap = { 'openai': 'https://api.openai.com/v1', 'anthropic': 'https://api.anthropic.com/v1', 'google': 'https://generativelanguage.googleapis.com/v1beta', 'aliyun': 'https://dashscope.aliyuncs.com/compatible-mode/v1', 'baidu': 'https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop', 'zhipu': 'https://open.bigmodel.cn/api/paas/v4', 'custom': '' }; const models = modelMap[selectedProvider] || ['custom-model']; modelSelect.innerHTML = models.map(m => ``).join(''); document.getElementById('api-base').value = apiBaseMap[selectedProvider] || ''; } async function loadConfig() { try { const response = await fetch(API_BASE); const result = await response.json(); if (result.success && result.data) { currentConfig = result.data; populateForm(currentConfig); renderModelsList(currentConfig.models || []); } } catch (error) { console.error('加载配置失败:', error); } } function populateForm(config) { if (config.models && config.models.length > 0) { const activeModel = config.models.find(m => m.enabled) || config.models[0]; document.getElementById('api-provider').value = activeModel.provider || 'openai'; document.getElementById('api-key').value = activeModel.api_key || ''; document.getElementById('api-base').value = activeModel.api_base || ''; document.getElementById('model-id').value = activeModel.model_id || 'gpt-4o'; document.getElementById('temperature').value = activeModel.temperature || 0.7; document.getElementById('temp-value').textContent = activeModel.temperature || 0.7; document.getElementById('max-tokens').value = activeModel.max_tokens || 2000; } if (config.analysis_settings) { document.getElementById('enable-technical').checked = config.analysis_settings.enable_technical_analysis !== false; document.getElementById('enable-fundamental').checked = config.analysis_settings.enable_fundamental_analysis === true; document.getElementById('enable-sentiment').checked = config.analysis_settings.enable_sentiment_analysis === true; document.getElementById('risk-tolerance').value = config.analysis_settings.risk_tolerance || 'medium'; document.getElementById('max-position').value = config.analysis_settings.max_position_pct || 10; } } function renderModelsList(models) { const list = document.getElementById('models-list'); if (!models || models.length === 0) { list.innerHTML = '
暂无已保存的模型
'; return; } list.innerHTML = models.map((model, index) => `
${model.model_name || model.model_id}
${getProviderName(model.provider || model.api_base)}
${!model.enabled ? `` : '默认'}
`).join(''); } function getProviderName(apiBase) { const map = { 'openai': 'OpenAI', 'anthropic': 'Anthropic', 'google': 'Google', 'aliyun': '阿里云', 'baidu': '百度', 'zhipu': '智谱' }; return map[apiBase] || apiBase; } function toggleApiKeyVisibility() { const input = document.getElementById('api-key'); const icon = document.querySelector('.toggle-visibility i'); if (input.type === 'password') { input.type = 'text'; icon.className = 'fas fa-eye-slash'; } else { input.type = 'password'; icon.className = 'fas fa-eye'; } } async function testConnection() { const resultEl = document.getElementById('test-result'); resultEl.textContent = '测试中...'; resultEl.className = 'test-result'; const config = { model_name: document.getElementById('model-id').value, api_key: document.getElementById('api-key').value, api_base: document.getElementById('api-base').value, model_id: document.getElementById('model-id').value, temperature: parseFloat(document.getElementById('temperature').value), max_tokens: parseInt(document.getElementById('max-tokens').value), enabled: true }; try { const response = await fetch(`${API_BASE}/test`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(config) }); const data = await response.json(); if (data.success) { resultEl.textContent = '✓ 连接成功'; resultEl.className = 'test-result success'; } else { resultEl.textContent = '✗ ' + data.message; resultEl.className = 'test-result error'; } } catch (error) { resultEl.textContent = '✗ 连接失败: ' + error.message; resultEl.className = 'test-result error'; } } async function saveConfig() { const models = currentConfig?.models || []; const existingIndex = models.findIndex(m => m.provider === selectedProvider); const newModel = { model_name: document.getElementById('model-id').value, provider: selectedProvider, api_key: document.getElementById('api-key').value, api_base: document.getElementById('api-base').value, model_id: document.getElementById('model-id').value, temperature: parseFloat(document.getElementById('temperature').value), max_tokens: parseInt(document.getElementById('max-tokens').value), enabled: true }; if (existingIndex >= 0) { models[existingIndex] = { ...models[existingIndex], ...newModel }; } else { models.push(newModel); } const config = { models: models, active_model: selectedProvider, analysis_settings: { enable_technical_analysis: document.getElementById('enable-technical').checked, enable_fundamental_analysis: document.getElementById('enable-fundamental').checked, enable_sentiment_analysis: document.getElementById('enable-sentiment').checked, risk_tolerance: document.getElementById('risk-tolerance').value, max_position_pct: parseInt(document.getElementById('max-position').value) } }; try { const response = await fetch(API_BASE, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(config) }); const data = await response.json(); if (data.success) { alert('配置保存成功!'); currentConfig = config; renderModelsList(models); } else { alert('保存失败: ' + data.message); } } catch (error) { alert('保存失败: ' + error.message); } } function setActiveModel(index) { if (!currentConfig || !currentConfig.models) return; currentConfig.models.forEach((m, i) => { m.enabled = i === index; }); saveConfig(); } function deleteModel(index) { if (!confirm('确定要删除这个模型吗?')) return; if (!currentConfig || !currentConfig.models) return; currentConfig.models.splice(index, 1); saveConfig(); } function addNewModel() { document.getElementById('api-key').value = ''; document.getElementById('api-key').focus(); }