v2.0.0 大模型驱动版:移除智能体,直接调用大模型接口
- 核心改造:不再使用 openclaw 智能体执行处理步骤,改为直接调用大模型接口 - 新增 services/llm_client.py:OpenAI 兼容接口客户端,支持多模型配置管理 - 步骤4/5 由大模型直接完成(提取产品数据、填充字段) - 步骤6 改为直接调用 ParamHub API 提交审核 - 新增 llm_configs 数据库表,默认配置 unsloth/Qwen3.6-27B-Q4_K_M (262144上下文) - 新增 /api/llm 配置管理 API:增删改查、切换激活、测试连接 - 前端首页新增「大模型配置」面板,可随时新增/切换模型 - 处理步骤名称更新为「大模型」版
This commit is contained in:
+308
-1
@@ -8,6 +8,7 @@ let currentArticleId = null;
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
refreshData();
|
||||
loadConfig();
|
||||
loadLlmConfigs();
|
||||
|
||||
// 搜索框事件
|
||||
document.getElementById('article-search').addEventListener('input', (e) => {
|
||||
@@ -878,4 +879,310 @@ async function saveSearchResultToLibrary() {
|
||||
closeModal('search-result-modal');
|
||||
loadArticles();
|
||||
loadStats();
|
||||
}
|
||||
}
|
||||
// ========== 大模型配置管理 ==========
|
||||
|
||||
let llmEditId = null; // 当前编辑的配置ID
|
||||
|
||||
// 加载大模型配置列表
|
||||
async function loadLlmConfigs() {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/api/llm/configs`);
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
displayLlmConfigs(data.configs);
|
||||
updateActiveModelBadge(data.configs);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载大模型配置失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 显示大模型配置列表
|
||||
function displayLlmConfigs(configs) {
|
||||
const container = document.getElementById('llm-config-table');
|
||||
|
||||
if (!configs || configs.length === 0) {
|
||||
container.innerHTML = '<tr><td colspan="6" class="empty-text">暂无大模型配置</td></tr>';
|
||||
return;
|
||||
}
|
||||
|
||||
container.innerHTML = configs.map(cfg => `
|
||||
<tr>
|
||||
<td>
|
||||
<strong>${escapeHtml(cfg.name)}</strong>
|
||||
${cfg.is_active ? ' <span class="badge badge-active">当前</span>' : ''}
|
||||
</td>
|
||||
<td><code>${escapeHtml(cfg.base_url)}</code></td>
|
||||
<td><code>${escapeHtml(cfg.model_name)}</code></td>
|
||||
<td>${cfg.max_context ? Number(cfg.max_context).toLocaleString() : '-'}</td>
|
||||
<td>
|
||||
${cfg.is_active
|
||||
? '<span class="status-badge"><span class="status-dot green"></span> 使用中</span>'
|
||||
: '<span class="status-badge"><span class="status-dot gray"></span> 未启用</span>'}
|
||||
</td>
|
||||
<td>
|
||||
<div class="action-buttons">
|
||||
${!cfg.is_active ? `
|
||||
<button onclick="activateLlmConfig(${cfg.id})" class="btn btn-sm btn-success" title="切换为当前使用">
|
||||
<i class="ri-switch-line"></i> 启用
|
||||
</button>
|
||||
` : ''}
|
||||
<button onclick="editLlmConfig(${cfg.id})" class="btn btn-sm btn-secondary" title="编辑">
|
||||
<i class="ri-edit-line"></i>
|
||||
</button>
|
||||
${!cfg.is_active ? `
|
||||
<button onclick="deleteLlmConfig(${cfg.id})" class="btn btn-sm btn-danger" title="删除">
|
||||
<i class="ri-delete-bin-line"></i>
|
||||
</button>
|
||||
` : ''}
|
||||
<button onclick="testLlmConfigById(${cfg.id})" class="btn btn-sm btn-secondary" title="测试连接">
|
||||
<i class="ri-connection-line"></i>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
// 更新顶部激活模型徽章
|
||||
function updateActiveModelBadge(configs) {
|
||||
const active = (configs || []).find(c => c.is_active);
|
||||
const nameEl = document.getElementById('active-model-name');
|
||||
if (active) {
|
||||
nameEl.textContent = `${active.name} (${active.model_name})`;
|
||||
} else {
|
||||
nameEl.textContent = '未配置大模型';
|
||||
}
|
||||
}
|
||||
|
||||
// 打开新增大模型模态框
|
||||
function showAddLlmModal() {
|
||||
llmEditId = null;
|
||||
document.getElementById('add-llm-modal').classList.add('active');
|
||||
|
||||
// 清空表单
|
||||
document.getElementById('llm-name').value = '';
|
||||
document.getElementById('llm-base-url').value = 'http://192.168.2.7:18003/v1';
|
||||
document.getElementById('llm-api-key').value = 'sk-xxxx';
|
||||
document.getElementById('llm-model-name').value = 'unsloth/Qwen3.6-27B-Q4_K_M';
|
||||
document.getElementById('llm-max-context').value = 262144;
|
||||
|
||||
// 重置按钮文字
|
||||
const modalTitle = document.querySelector('#add-llm-modal .modal-header h3');
|
||||
modalTitle.innerHTML = '<i class="ri-openai-line"></i> 新增大模型配置';
|
||||
const saveBtn = document.querySelector('#add-llm-modal .modal-footer .btn-primary');
|
||||
saveBtn.textContent = '添加';
|
||||
saveBtn.onclick = addLlmConfig;
|
||||
}
|
||||
|
||||
// 编辑大模型配置
|
||||
async function editLlmConfig(configId) {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/api/llm/configs`);
|
||||
const data = await response.json();
|
||||
|
||||
if (!data.success) return;
|
||||
|
||||
const cfg = data.configs.find(c => c.id === configId);
|
||||
if (!cfg) return;
|
||||
|
||||
llmEditId = configId;
|
||||
document.getElementById('llm-name').value = cfg.name || '';
|
||||
document.getElementById('llm-base-url').value = cfg.base_url || '';
|
||||
document.getElementById('llm-api-key').value = cfg.api_key || '';
|
||||
document.getElementById('llm-model-name').value = cfg.model_name || '';
|
||||
document.getElementById('llm-max-context').value = cfg.max_context || 262144;
|
||||
|
||||
const modalTitle = document.querySelector('#add-llm-modal .modal-header h3');
|
||||
modalTitle.innerHTML = '<i class="ri-edit-line"></i> 编辑大模型配置';
|
||||
const saveBtn = document.querySelector('#add-llm-modal .modal-footer .btn-primary');
|
||||
saveBtn.textContent = '保存修改';
|
||||
saveBtn.onclick = updateLlmConfig;
|
||||
|
||||
document.getElementById('add-llm-modal').classList.add('active');
|
||||
} catch (error) {
|
||||
showToast('加载配置失败', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// 添加大模型配置
|
||||
async function addLlmConfig() {
|
||||
const name = document.getElementById('llm-name').value.trim();
|
||||
const baseUrl = document.getElementById('llm-base-url').value.trim();
|
||||
const apiKey = document.getElementById('llm-api-key').value.trim();
|
||||
const modelName = document.getElementById('llm-model-name').value.trim();
|
||||
const maxContext = parseInt(document.getElementById('llm-max-context').value) || 262144;
|
||||
|
||||
if (!name || !baseUrl || !modelName) {
|
||||
showToast('请填写必填字段(名称、接口地址、模型名称)', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/api/llm/configs`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ name, base_url: baseUrl, api_key: apiKey, model_name: modelName, max_context: maxContext })
|
||||
});
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
showToast('大模型配置已添加', 'success');
|
||||
closeModal('add-llm-modal');
|
||||
loadLlmConfigs();
|
||||
} else {
|
||||
showToast('添加失败: ' + data.error, 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
showToast('添加失败', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// 更新大模型配置
|
||||
async function updateLlmConfig() {
|
||||
if (!llmEditId) return;
|
||||
|
||||
const name = document.getElementById('llm-name').value.trim();
|
||||
const baseUrl = document.getElementById('llm-base-url').value.trim();
|
||||
const apiKey = document.getElementById('llm-api-key').value.trim();
|
||||
const modelName = document.getElementById('llm-model-name').value.trim();
|
||||
const maxContext = parseInt(document.getElementById('llm-max-context').value) || 262144;
|
||||
|
||||
if (!name || !baseUrl || !modelName) {
|
||||
showToast('请填写必填字段(名称、接口地址、模型名称)', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/api/llm/configs/${llmEditId}`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ name, base_url: baseUrl, api_key: apiKey, model_name: modelName, max_context: maxContext })
|
||||
});
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
showToast('配置已更新', 'success');
|
||||
closeModal('add-llm-modal');
|
||||
loadLlmConfigs();
|
||||
} else {
|
||||
showToast('更新失败: ' + data.error, 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
showToast('更新失败', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// 删除大模型配置
|
||||
async function deleteLlmConfig(configId) {
|
||||
if (!confirm('确定删除该大模型配置吗?')) return;
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/api/llm/configs/${configId}`, {
|
||||
method: 'DELETE'
|
||||
});
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
showToast('配置已删除', 'success');
|
||||
loadLlmConfigs();
|
||||
} else {
|
||||
showToast('删除失败: ' + data.error, 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
showToast('删除失败', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// 切换激活的大模型配置
|
||||
async function activateLlmConfig(configId) {
|
||||
if (!confirm('确定切换使用该大模型吗?后续处理将使用它执行所有智能体任务。')) return;
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/api/llm/configs/${configId}/activate`, {
|
||||
method: 'POST'
|
||||
});
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
showToast(data.message, 'success');
|
||||
loadLlmConfigs();
|
||||
} else {
|
||||
showToast('切换失败: ' + data.error, 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
showToast('切换失败', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// 测试当前表单中的连接
|
||||
async function testLlmConnection() {
|
||||
const name = document.getElementById('llm-name').value.trim();
|
||||
const baseUrl = document.getElementById('llm-base-url').value.trim();
|
||||
const apiKey = document.getElementById('llm-api-key').value.trim();
|
||||
const modelName = document.getElementById('llm-model-name').value.trim();
|
||||
|
||||
if (!baseUrl || !modelName) {
|
||||
showToast('请先填写接口地址和模型名称', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
const btn = document.getElementById('llm-test-btn');
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '<i class="ri-loader-4-line"></i> 测试中...';
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/api/llm/test`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
config: { base_url: baseUrl, api_key: apiKey, model_name: modelName }
|
||||
})
|
||||
});
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
showToast('连接成功: ' + data.message, 'success');
|
||||
} else {
|
||||
showToast('连接失败: ' + data.message, 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
showToast('测试失败', 'error');
|
||||
}
|
||||
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = '<i class="ri-connection-line"></i> 测试连接';
|
||||
}
|
||||
|
||||
// 测试指定配置的连接
|
||||
async function testLlmConfigById(configId) {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/api/llm/configs`);
|
||||
const data = await response.json();
|
||||
if (!data.success) return;
|
||||
|
||||
const cfg = data.configs.find(c => c.id === configId);
|
||||
if (!cfg) return;
|
||||
|
||||
showToast('正在测试连接...', '');
|
||||
|
||||
const testResp = await fetch(`${API_BASE}/api/llm/test`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
config: { base_url: cfg.base_url, api_key: cfg.api_key, model_name: cfg.model_name }
|
||||
})
|
||||
});
|
||||
const testData = await testResp.json();
|
||||
|
||||
if (testData.success) {
|
||||
showToast('连接成功: ' + testData.message, 'success');
|
||||
} else {
|
||||
showToast('连接失败: ' + testData.message, 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
showToast('测试失败', 'error');
|
||||
}
|
||||
}
|
||||
@@ -433,7 +433,7 @@ function showToast(message, type = '') {
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
// ===== 智能体任务模板 =====
|
||||
// ===== 大模型任务模板 =====
|
||||
|
||||
// 加载模板
|
||||
async function loadAgentTemplate() {
|
||||
|
||||
Reference in New Issue
Block a user