/** * API配置管理器 * 负责管理API配置弹窗和API相关设置 */ import { app } from "../../../../scripts/app.js"; import { logger } from '../utils/logger.js'; import { createSettingsDialog, createFormGroup, createInputGroup, createSelectGroup, createHorizontalFormGroup } from "./settings.js"; class APIConfigManager { constructor() { this.llmAllProviders = {}; this.visionAllProviders = {}; this.llmModel = null; this.llmApiKey = null; this.llmBaseUrl = null; this.visionModel = null; this.visionApiKey = null; this.visionBaseUrl = null; } /** * 显示API配置弹窗 */ showAPIConfigModal() { try { logger.debug('打开API配置弹窗'); createSettingsDialog({ title: 'API管理器', dialogClassName: 'api-config-dialog', disableBackdropAndCloseOnClickOutside: true, renderContent: (container) => { this._createAPIConfigUI(container); }, onSave: async () => { try { const container = document.querySelector('.settings-modal .p-dialog-content'); const controls = container.formControls; // 先保存当前显示的提供商配置 const currentLLMProvider = controls.llm.provider.value; this._saveLLMProviderConfig(currentLLMProvider); const currentVisionProvider = controls.vision.provider.value; this._saveVisionProviderConfig(currentVisionProvider); // 处理百度翻译配置 const baiduConfig = { app_id: controls.baidu.appId.value.trim(), }; const baiduSecretEl = controls.baidu.secret; const baiduSecretValue = baiduSecretEl.value.trim(); const isBaiduMasked = baiduSecretValue === '••••••••••••••••••••••••••••••••••••••••••••••••'; // 只有当密钥不是掩码,或者掩码不代表现有密钥时,才包含secret_key if (!isBaiduMasked || baiduSecretEl.dataset.hasKey !== 'true') { baiduConfig.secret_key = baiduSecretValue; } // 获取配置值 const config = { baidu: baiduConfig, llm: { current_provider: currentLLMProvider, providers: this.llmAllProviders || {} }, vision: { current_provider: currentVisionProvider, providers: this.visionAllProviders || {} } }; // 确保每个提供商都有配置 const providerList = ["zhipu", "siliconflow", "302ai", "ollama", "custom"]; providerList.forEach(provider => { // 确保LLM提供商配置存在 if (!config.llm.providers[provider]) { config.llm.providers[provider] = { model: provider === "zhipu" ? "glm-4-flash-250414" : (provider === "siliconflow" ? "Qwen/Qwen2.5-7B-Instruct" : ""), base_url: provider === "zhipu" ? "https://open.bigmodel.cn/api/paas/v4" : (provider === "siliconflow" ? "https://api.siliconflow.cn/v1" : ""), api_key: "", temperature: 0.7, max_tokens: 2000, top_p: 0.9 }; } // 确保视觉模型提供商配置存在 if (!config.vision.providers[provider]) { config.vision.providers[provider] = { model: provider === "zhipu" ? "glm-4v-flash" : (provider === "siliconflow" ? "THUDM/GLM-4.1V-9B-Thinking" : ""), base_url: provider === "zhipu" ? "https://open.bigmodel.cn/api/paas/v4/chat/completions" : (provider === "siliconflow" ? "https://api.siliconflow.cn/v1/chat/completions" : ""), api_key: "", temperature: 0.7, max_tokens: 2000, top_p: 0.9 }; } }); console.log("保存配置:", JSON.stringify(config)); // 调试信息 // 保存配置 await Promise.all([ fetch('/prompt_assistant/api/config/baidu_translate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(config.baidu) }), fetch('/prompt_assistant/api/config/llm', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(config.llm) }), fetch('/prompt_assistant/api/config/vision', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(config.vision) }) ]).then(responses => { // 检查所有响应是否成功 const failedResponses = responses.filter(response => !response.ok); if (failedResponses.length > 0) { // 至少有一个请求失败 throw new Error(`配置保存失败,服务器返回错误: ${failedResponses[0].status}`); } // 所有请求成功 app.extensionManager.toast.add({ severity: "success", summary: "配置已更新", life: 3000 }); }).catch(error => { // 显示错误提示 app.extensionManager.toast.add({ severity: "error", summary: "配置保存失败", detail: error.message || "保存配置时发生错误", life: 3000 }); // 重新抛出错误,以便外层catch可以处理 throw error; }); } catch (error) { // 这里捕获的是其他类型的错误,如代码执行错误 app.extensionManager.toast.add({ severity: "error", summary: "配置保存失败", detail: error.message || "保存配置时发生错误", life: 3000 }); throw error; } } }); } catch (error) { logger.error(`打开API配置弹窗失败: ${error.message}`); app.extensionManager.toast.add({ severity: "error", summary: "打开配置失败", detail: error.message || "打开配置弹窗过程中发生错误", life: 3000 }); } } /** * 创建API配置UI * @param {HTMLElement} container 容器元素 */ _createAPIConfigUI(container) { // 创建表单 const form = document.createElement('form'); form.onsubmit = (e) => e.preventDefault(); form.className = 'api-config-form'; // 1. 百度翻译 API 配置 const baiduSection = this._createBaiduSection(); // 2. LLM 配置 const llmSection = this._createLLMSection(); // 3. 视觉模型配置 const visionSection = this._createVisionSection(); // 添加所有部分到表单 form.appendChild(baiduSection); form.appendChild(llmSection); form.appendChild(visionSection); container.appendChild(form); // 加载已有配置 this._loadAPIConfig(container); } /** * 创建百度翻译API配置部分 * @returns {HTMLElement} 百度翻译API配置部分的DOM元素 */ _createBaiduSection() { // 百度翻译 API 配置 const baiduSection = createFormGroup('百度翻译配置', [ { text: '百度翻译API申请', url: 'https://fanyi-api.baidu.com/' } ]); // 创建输入框 const baiduAppId = createInputGroup('', '请输入百度翻译 AppID'); const baiduSecret = createInputGroup('', '请输入百度翻译密钥'); // 创建水平布局组 const baiduGroup = createHorizontalFormGroup([ { label: 'AppID', element: baiduAppId.input }, { label: 'Secret Key', element: baiduSecret.input } ]); baiduSection.appendChild(baiduGroup); // 保存输入框引用,便于后续获取值 this.baiduAppId = baiduAppId.input; this.baiduSecret = baiduSecret.input; return baiduSection; } /** * 创建LLM配置部分 * @returns {HTMLElement} LLM配置部分的DOM元素 */ _createLLMSection() { // LLM 配置 const llmSection = createFormGroup('大语言模型配置', [ { text: '智谱', url: 'https://www.bigmodel.cn/invite?icode=Wz1tQAT40T9M8vwp%2F1db7nHEaazDlIZGj9HxftzTbt4%3D' }, { text: '硅基流动', url: 'https://cloud.siliconflow.cn/i/FCDL2zBQ' }, { text: '302.AI', url: 'https://share.302.ai/JrO51c' } ], { prefixText: '大模型API申请:' }); // 创建选择器和输入框 const llmProvider = createSelectGroup('', [ { value: 'zhipu', text: '智谱' }, { value: 'siliconflow', text: '硅基流动' }, { value: '302ai', text: '302.AI' }, { value: 'ollama', text: 'Ollama' }, { value: 'custom', text: '自定义' } ]); const llmModelInput = createInputGroup('', '请输入模型名称'); this.llmModel = llmModelInput.input; // 保存引用以供外部函数使用 // 创建base_url输入框(初始隐藏) const llmBaseUrlInput = createInputGroup('Base URL', '请输入API基础URL,例如: https://api.example.com/v1'); this.llmBaseUrl = llmBaseUrlInput.input; // 保存引用以供外部函数使用 llmBaseUrlInput.group.style.display = 'none'; // 初始隐藏 // 在base_url输入框下添加提示信息 const llmBaseUrlHint = document.createElement('div'); llmBaseUrlHint.className = 'base-url-hint'; llmBaseUrlHint.style.fontSize = '12px'; llmBaseUrlHint.style.color = '#666'; llmBaseUrlHint.style.marginTop = '4px'; llmBaseUrlHint.style.marginBottom = '8px'; llmBaseUrlHint.textContent = '注意: 请输入基础URL,不要包含/chat/completions路径'; llmBaseUrlInput.group.appendChild(llmBaseUrlHint); // 创建水平布局组 const llmProviderGroup = createHorizontalFormGroup([ { label: '模型提供商', element: llmProvider.group }, { label: '模型名称', element: this.llmModel } ]); // API Key 保持单独一行 const llmApiKeyInput = createInputGroup('API Key', '请输入模型 API Key'); this.llmApiKey = llmApiKeyInput.input; // 保存引用以供外部函数使用 // ---模型参数配置--- // 创建温度和最大token输入框 const llmTemperatureInput = createInputGroup('', '0.1-2.0,控制输出随机性'); llmTemperatureInput.input.type = 'number'; llmTemperatureInput.input.min = '0.1'; llmTemperatureInput.input.max = '2.0'; llmTemperatureInput.input.step = '0.1'; llmTemperatureInput.input.value = '0.7'; // 默认值 this.llmTemperature = llmTemperatureInput.input; const llmMaxTokensInput = createInputGroup('', '1-32000,控制最大输出长度'); llmMaxTokensInput.input.type = 'number'; llmMaxTokensInput.input.min = '1'; llmMaxTokensInput.input.max = '32000'; llmMaxTokensInput.input.step = '1'; llmMaxTokensInput.input.value = '2000'; // 默认值 this.llmMaxTokens = llmMaxTokensInput.input; const llmTopPInput = createInputGroup('', '0.1-1.0,控制核采样范围'); llmTopPInput.input.type = 'number'; llmTopPInput.input.min = '0.1'; llmTopPInput.input.max = '1.0'; llmTopPInput.input.step = '0.1'; llmTopPInput.input.value = '0.9'; // 默认值 this.llmTopP = llmTopPInput.input; // 创建模型参数水平布局组(三个参数在同一行) const llmParamsGroup = createHorizontalFormGroup([ { label: '温度 (Temperature)', element: this.llmTemperature }, { label: '核采样 (Top-P)', element: this.llmTopP }, { label: '最大Token数', element: this.llmMaxTokens } ]); llmSection.appendChild(llmProviderGroup); llmSection.appendChild(llmBaseUrlInput.group); // 添加base_url输入框 llmSection.appendChild(llmApiKeyInput.group); llmSection.appendChild(llmParamsGroup); // 添加模型参数配置(三个参数在同一行) // 监听LLM提供商选择变化 llmProvider.select.addEventListener('change', () => { const oldProvider = llmProvider.select.dataset.previousValue; const provider = llmProvider.select.value; // 如果存在旧提供商,保存其配置 if (oldProvider) { this._saveLLMProviderConfig(oldProvider); } // 根据提供商类型显示/隐藏 base_url 与 API Key if (provider === 'custom') { llmBaseUrlInput.group.style.display = 'block'; llmApiKeyInput.group.style.display = 'block'; } else if (provider === '302ai') { llmBaseUrlInput.group.style.display = 'none'; llmApiKeyInput.group.style.display = 'block'; } else if (provider === 'ollama') { llmBaseUrlInput.group.style.display = 'none'; llmApiKeyInput.group.style.display = 'none'; } else { llmBaseUrlInput.group.style.display = 'none'; llmApiKeyInput.group.style.display = 'block'; } // 加载选定提供商的配置 if (this.llmAllProviders && this.llmAllProviders[provider]) { const providerConfig = this.llmAllProviders[provider]; this.llmModel.value = providerConfig.model || ''; // 设置API密钥(使用掩码) if (providerConfig.api_key || providerConfig.has_key) { this._setMaskedValue(this.llmApiKey, providerConfig.api_key || "dummy-key"); } else { this.llmApiKey.value = ''; this.llmApiKey.dataset.hasKey = 'false'; } // 加载模型参数 this.llmTemperature.value = providerConfig.temperature || '0.7'; this.llmMaxTokens.value = providerConfig.max_tokens || '2000'; this.llmTopP.value = providerConfig.top_p || '0.9'; if (provider === 'custom') { this.llmBaseUrl.value = providerConfig.base_url || ''; } else if (provider === '302ai') { // 302.AI 为内置 base_url,不需要填写 this.llmBaseUrl.value = ''; } else if (provider === 'ollama') { // Ollama base_url 与 api_key 内置 this.llmBaseUrl.value = ''; this._setMaskedValue(this.llmApiKey, 'ollama'); } } else { // 设置默认模型名称 if (provider === 'zhipu') { this.llmModel.value = 'glm-4-flash-250414'; } else if (provider === 'siliconflow') { this.llmModel.value = 'Qwen/Qwen2.5-7B-Instruct'; } else if (provider === '302ai') { this.llmModel.value = 'Qwen/Qwen2.5-7B-Instruct'; } else if (provider === 'ollama') { this.llmModel.value = 'llama3.1'; } else { this.llmModel.value = ''; } // 清空API Key和base_url this.llmApiKey.value = ''; this.llmApiKey.dataset.hasKey = 'false'; // 设置默认模型参数 this.llmTemperature.value = '0.7'; this.llmMaxTokens.value = '2000'; this.llmTopP.value = '0.9'; if (provider === 'custom') { this.llmBaseUrl.value = ''; } } // 记录当前选择的提供商 llmProvider.select.dataset.previousValue = provider; }); // 保存选择器引用,便于后续获取值 this.llmProvider = llmProvider.select; return llmSection; } /** * 创建视觉模型配置部分 * @returns {HTMLElement} 视觉模型配置部分的DOM元素 */ _createVisionSection() { // 视觉模型配置 const visionSection = createFormGroup('视觉模型配置', []); // 创建选择器和输入框 const visionProvider = createSelectGroup('', [ { value: 'zhipu', text: '智谱' }, { value: 'siliconflow', text: '硅基流动' }, { value: '302ai', text: '302.AI' }, { value: 'ollama', text: 'Ollama' }, { value: 'custom', text: '自定义' } ]); const visionModelInput = createInputGroup('', '请输入模型名称'); this.visionModel = visionModelInput.input; // 保存引用以供外部函数使用 // 创建base_url输入框(初始隐藏) const visionBaseUrlInput = createInputGroup('Base URL', '请输入API基础URL,例如: https://api.example.com/v1'); this.visionBaseUrl = visionBaseUrlInput.input; // 保存引用以供外部函数使用 visionBaseUrlInput.group.style.display = 'none'; // 初始隐藏 // 在base_url输入框下添加提示信息 const visionBaseUrlHint = document.createElement('div'); visionBaseUrlHint.className = 'base-url-hint'; visionBaseUrlHint.style.fontSize = '12px'; visionBaseUrlHint.style.color = '#666'; visionBaseUrlHint.style.marginTop = '4px'; visionBaseUrlHint.style.marginBottom = '8px'; visionBaseUrlHint.textContent = '注意: 请输入基础URL,不要包含/chat/completions路径'; visionBaseUrlInput.group.appendChild(visionBaseUrlHint); // 创建水平布局组 const visionProviderGroup = createHorizontalFormGroup([ { label: '模型提供商', element: visionProvider.group }, { label: '模型名称', element: this.visionModel } ]); // API Key 保持单独一行 const visionApiKeyInput = createInputGroup('API Key', '请输入模型 API Key'); this.visionApiKey = visionApiKeyInput.input; // 保存引用以供外部函数使用 // ---视觉模型参数配置--- // 创建温度和最大token输入框 const visionTemperatureInput = createInputGroup('', '0.1-2.0,控制输出随机性'); visionTemperatureInput.input.type = 'number'; visionTemperatureInput.input.min = '0.1'; visionTemperatureInput.input.max = '2.0'; visionTemperatureInput.input.step = '0.1'; visionTemperatureInput.input.value = '0.7'; // 默认值 this.visionTemperature = visionTemperatureInput.input; const visionMaxTokensInput = createInputGroup('', '1-32000,控制最大输出长度'); visionMaxTokensInput.input.type = 'number'; visionMaxTokensInput.input.min = '1'; visionMaxTokensInput.input.max = '32000'; visionMaxTokensInput.input.step = '1'; visionMaxTokensInput.input.value = '2000'; // 默认值 this.visionMaxTokens = visionMaxTokensInput.input; const visionTopPInput = createInputGroup('', '0.1-1.0,控制核采样范围'); visionTopPInput.input.type = 'number'; visionTopPInput.input.min = '0.1'; visionTopPInput.input.max = '1.0'; visionTopPInput.input.step = '0.1'; visionTopPInput.input.value = '0.9'; // 默认值 this.visionTopP = visionTopPInput.input; // 创建视觉模型参数水平布局组(三个参数在同一行) const visionParamsGroup = createHorizontalFormGroup([ { label: '温度 (Temperature)', element: this.visionTemperature }, { label: '核采样 (Top-P)', element: this.visionTopP }, { label: '最大Token数', element: this.visionMaxTokens } ]); visionSection.appendChild(visionProviderGroup); visionSection.appendChild(visionBaseUrlInput.group); // 添加base_url输入框 visionSection.appendChild(visionApiKeyInput.group); visionSection.appendChild(visionParamsGroup); // 添加视觉模型参数配置(三个参数在同一行) // 监听视觉模型提供商选择变化 visionProvider.select.addEventListener('change', () => { const oldProvider = visionProvider.select.dataset.previousValue; const provider = visionProvider.select.value; // 如果存在旧提供商,保存其配置 if (oldProvider) { this._saveVisionProviderConfig(oldProvider); } // 根据提供商类型显示/隐藏 base_url 与 API Key if (provider === 'custom') { visionBaseUrlInput.group.style.display = 'block'; visionApiKeyInput.group.style.display = 'block'; } else if (provider === '302ai') { visionBaseUrlInput.group.style.display = 'none'; visionApiKeyInput.group.style.display = 'block'; } else if (provider === 'ollama') { visionBaseUrlInput.group.style.display = 'none'; visionApiKeyInput.group.style.display = 'none'; } else { visionBaseUrlInput.group.style.display = 'none'; visionApiKeyInput.group.style.display = 'block'; } // 加载选定提供商的配置 if (this.visionAllProviders && this.visionAllProviders[provider]) { const providerConfig = this.visionAllProviders[provider]; this.visionModel.value = providerConfig.model || ''; // 设置API密钥(使用掩码) if (providerConfig.api_key || providerConfig.has_key) { this._setMaskedValue(this.visionApiKey, providerConfig.api_key || "dummy-key"); } else { this.visionApiKey.value = ''; this.visionApiKey.dataset.hasKey = 'false'; } // 加载模型参数 this.visionTemperature.value = providerConfig.temperature || '0.7'; this.visionMaxTokens.value = providerConfig.max_tokens || '2000'; this.visionTopP.value = providerConfig.top_p || '0.9'; if (provider === 'custom') { this.visionBaseUrl.value = providerConfig.base_url || ''; } else if (provider === '302ai') { this.visionBaseUrl.value = ''; } else if (provider === 'ollama') { this.visionBaseUrl.value = ''; this._setMaskedValue(this.visionApiKey, 'ollama'); } } else { // 设置默认模型名称 if (provider === 'zhipu') { this.visionModel.value = 'glm-4v-flash'; } else if (provider === 'siliconflow') { this.visionModel.value = 'THUDM/GLM-4.1V-9B-Thinking'; } else if (provider === '302ai') { this.visionModel.value = 'THUDM/GLM-4.1V-9B-Thinking'; } else if (provider === 'ollama') { this.visionModel.value = 'llava:latest'; } else { this.visionModel.value = ''; } // 清空API Key和base_url this.visionApiKey.value = ''; this.visionApiKey.dataset.hasKey = 'false'; // 设置默认模型参数 this.visionTemperature.value = '0.7'; this.visionMaxTokens.value = '2000'; this.visionTopP.value = '0.9'; if (provider === 'custom') { this.visionBaseUrl.value = ''; } } // 记录当前选择的提供商 visionProvider.select.dataset.previousValue = provider; }); // 保存选择器引用,便于后续获取值 this.visionProvider = visionProvider.select; return visionSection; } /** * 设置掩码值,同时保存原始状态 * @param {HTMLInputElement} input 输入元素 * @param {string} value 原始值 */ _setMaskedValue(input, value) { const hasValue = value && value.trim() !== ''; if (hasValue) { // 显示掩码 input.value = '••••••••••••••••••••••••••••••••••••••••••••••••'; // 标记为已设置 input.dataset.hasKey = 'true'; } else { input.value = ''; input.dataset.hasKey = 'false'; } // 添加焦点事件处理 if (!input._maskedHandlersAdded) { input.addEventListener('focus', function () { if (this.dataset.hasKey === 'true' && this.value === '••••••••••••••••••••••••••••••••••••••••••••••••') { this.value = ''; } }); input.addEventListener('blur', function () { if (this.dataset.hasKey === 'true' && this.value === '') { this.value = '••••••••••••••••••••••••••••••••••••••••••••••••'; } }); input._maskedHandlersAdded = true; } } /** * 加载API配置 * @param {HTMLElement} container 容器元素 */ async _loadAPIConfig(container) { try { const [baiduConfig, llmConfig, visionConfig] = await Promise.all([ fetch('/prompt_assistant/api/config/baidu_translate').then(r => r.json()), fetch('/prompt_assistant/api/config/llm').then(r => r.json()), fetch('/prompt_assistant/api/config/vision').then(r => r.json()) ]); // 设置百度翻译配置 if (baiduConfig.app_id) { this.baiduAppId.value = baiduConfig.app_id; } if (baiduConfig.secret_key) { // 使用掩码显示密钥 this._setMaskedValue(this.baiduSecret, baiduConfig.secret_key); } // 存储所有提供商的配置 this.llmAllProviders = llmConfig.providers || {}; this.visionAllProviders = visionConfig.providers || {}; // 为所有提供商预处理API密钥状态 const providerList = ["zhipu", "siliconflow", "302ai", "ollama", "custom"]; // 处理LLM提供商的API密钥 providerList.forEach(provider => { if (this.llmAllProviders[provider]) { if (provider === 'ollama') { // 内置API Key,不需要展示输入 this.llmAllProviders[provider].api_key = 'ollama'; this.llmAllProviders[provider].has_key = true; } else if (this.llmAllProviders[provider].api_key) { // 保留API密钥值,但不在此处设置掩码(在切换提供商时会设置) this.llmAllProviders[provider].has_key = true; } } }); // 处理视觉模型提供商的API密钥 providerList.forEach(provider => { if (this.visionAllProviders[provider] && this.visionAllProviders[provider].api_key) { // 保留API密钥值,但不在此处设置掩码(在切换提供商时会设置) this.visionAllProviders[provider].has_key = true; } }); // 设置 LLM 配置 if (llmConfig.provider) { this.llmProvider.value = llmConfig.provider; this.llmProvider.dispatchEvent(new Event('change')); } if (llmConfig.model) { this.llmModel.value = llmConfig.model; } if (llmConfig.api_key) { // 使用掩码显示密钥 this._setMaskedValue(this.llmApiKey, llmConfig.api_key); } // 加载模型参数 if (llmConfig.temperature !== undefined) { this.llmTemperature.value = llmConfig.temperature; } if (llmConfig.max_tokens !== undefined) { this.llmMaxTokens.value = llmConfig.max_tokens; } if (llmConfig.top_p !== undefined) { this.llmTopP.value = llmConfig.top_p; } if (llmConfig.base_url && llmConfig.provider === 'custom') { this.llmBaseUrl.value = llmConfig.base_url; } // 设置视觉模型配置 if (visionConfig.provider) { this.visionProvider.value = visionConfig.provider; this.visionProvider.dispatchEvent(new Event('change')); } if (visionConfig.model) { this.visionModel.value = visionConfig.model; } if (visionConfig.api_key) { // 使用掩码显示密钥 this._setMaskedValue(this.visionApiKey, visionConfig.api_key); } // 加载视觉模型参数 if (visionConfig.temperature !== undefined) { this.visionTemperature.value = visionConfig.temperature; } if (visionConfig.max_tokens !== undefined) { this.visionMaxTokens.value = visionConfig.max_tokens; } if (visionConfig.top_p !== undefined) { this.visionTopP.value = visionConfig.top_p; } if (visionConfig.base_url && visionConfig.provider === 'custom') { this.visionBaseUrl.value = visionConfig.base_url; } // 将表单控件暴露给保存回调 container.formControls = { baidu: { appId: this.baiduAppId, secret: this.baiduSecret }, llm: { provider: this.llmProvider, temperature: this.llmTemperature, top_p: this.llmTopP, max_tokens: this.llmMaxTokens }, vision: { provider: this.visionProvider, temperature: this.visionTemperature, top_p: this.visionTopP, max_tokens: this.visionMaxTokens } }; } catch (error) { logger.error("加载API配置失败:", error); app.extensionManager.toast.add({ severity: "error", summary: "加载失败", detail: error.message || "加载API配置过程中发生错误", life: 3000 }); } } /** * 保存LLM提供商配置 * @param {string} provider 提供商名称 */ _saveLLMProviderConfig(provider) { if (!this.llmAllProviders) this.llmAllProviders = {}; // 获取当前配置 let baseUrl = ''; if (provider === 'custom') { baseUrl = this.llmBaseUrl ? this.llmBaseUrl.value.trim() : ''; // 确保base_url不以/结尾 if (baseUrl && baseUrl.endsWith('/')) { baseUrl = baseUrl.slice(0, -1); } // 确保base_url不以/chat/completions结尾 if (baseUrl && baseUrl.endsWith('/chat/completions')) { baseUrl = baseUrl.slice(0, -16); // 移除'/chat/completions' } } else { baseUrl = provider === 'zhipu' ? 'https://open.bigmodel.cn/api/paas/v4' : (provider === 'siliconflow' ? 'https://api.siliconflow.cn/v1' : (provider === '302ai' ? 'https://api.302.ai/v1' : (provider === 'ollama' ? 'http://localhost:11434/v1' : ''))); } // 检查API密钥是否为掩码 let apiKey = this.llmApiKey.value.trim(); const isMasked = apiKey === '••••••••••••••••••••••••••••••••••••••••••••••••'; // 创建提供商配置对象 const providerConfig = { model: this.llmModel.value.trim(), base_url: baseUrl, temperature: parseFloat(this.llmTemperature.value) || 0.7, max_tokens: parseInt(this.llmMaxTokens.value) || 2000, top_p: parseFloat(this.llmTopP.value) || 0.9 }; // 保留现有配置中的has_key标记 if (this.llmAllProviders[provider] && this.llmAllProviders[provider].has_key) { providerConfig.has_key = true; } // 只有当API密钥不是掩码时才更新 if (!isMasked || this.llmApiKey.dataset.hasKey !== 'true') { providerConfig.api_key = apiKey; // 如果输入了新的API密钥,设置has_key标记 if (apiKey) { providerConfig.has_key = true; } else { // 如果清空了API密钥,移除has_key标记 providerConfig.has_key = false; } } // 更新配置 this.llmAllProviders[provider] = providerConfig; logger.debug(`已保存 ${provider} 提供商的LLM配置`); } /** * 保存视觉模型提供商配置 * @param {string} provider 提供商名称 */ _saveVisionProviderConfig(provider) { if (!this.visionAllProviders) this.visionAllProviders = {}; // 获取当前配置 let baseUrl = ''; if (provider === 'custom') { baseUrl = this.visionBaseUrl ? this.visionBaseUrl.value.trim() : ''; // 确保base_url不以/结尾 if (baseUrl && baseUrl.endsWith('/')) { baseUrl = baseUrl.slice(0, -1); } // 确保base_url不以/chat/completions结尾 if (baseUrl && baseUrl.endsWith('/chat/completions')) { baseUrl = baseUrl.slice(0, -16); // 移除'/chat/completions' } } else { baseUrl = provider === 'zhipu' ? 'https://open.bigmodel.cn/api/paas/v4' : (provider === 'siliconflow' ? 'https://api.siliconflow.cn/v1' : (provider === '302ai' ? 'https://api.302.ai/v1' : (provider === 'ollama' ? 'http://localhost:11434/v1' : ''))); } // 检查API密钥是否为掩码 let apiKey = this.visionApiKey.value.trim(); const isMasked = apiKey === '••••••••••••••••••••••••••••••••••••••••••••••••'; // 创建提供商配置对象 const providerConfig = { model: this.visionModel.value.trim(), base_url: baseUrl, temperature: parseFloat(this.visionTemperature.value) || 0.7, max_tokens: parseInt(this.visionMaxTokens.value) || 2000, top_p: parseFloat(this.visionTopP.value) || 0.9 }; // 保留现有配置中的has_key标记 if (this.visionAllProviders[provider] && this.visionAllProviders[provider].has_key) { providerConfig.has_key = true; } // 只有当API密钥不是掩码时才更新 if (!isMasked || this.visionApiKey.dataset.hasKey !== 'true') { providerConfig.api_key = apiKey; // 如果输入了新的API密钥,设置has_key标记 if (apiKey) { providerConfig.has_key = true; } else { // 如果清空了API密钥,移除has_key标记 providerConfig.has_key = false; } } // 更新配置 this.visionAllProviders[provider] = providerConfig; logger.debug(`已保存 ${provider} 提供商的视觉模型配置`); } } // 导出API配置管理器实例 export const apiConfigManager = new APIConfigManager();