feat(config): 添加提供商最大错误次数配置和动态重载功能
新增MAX_ERROR_COUNT配置项用于控制提供商连续错误次数阈值, 当达到此阈值时提供商将被标记为不健康。同时实现配置动态重载功能, 支持在不重启服务的情况下更新配置,包括提供商池管理和UI配置界面的实时同步。 - 在config.json.example中添加MAX_ERROR_COUNT默认值 - 在config-manager.js中实现命令行参数--max-error-count解析 - 在service-manager.js中将maxErrorCount传递给ProviderPoolManager - 在ui-manager.js中实现reloadConfig函数和配置重载API - 更新前端配置管理界面,添加maxErrorCount输入控件 - 改进文件上传处理,支持模态框提供商类型识别 - 在所有提供商操作后自动触发配置重载
This commit is contained in:
parent
1dde8b5a74
commit
7746e94154
9 changed files with 84 additions and 35 deletions
|
|
@ -21,5 +21,6 @@
|
||||||
"REQUEST_BASE_DELAY": 1000,
|
"REQUEST_BASE_DELAY": 1000,
|
||||||
"CRON_NEAR_MINUTES": 1,
|
"CRON_NEAR_MINUTES": 1,
|
||||||
"CRON_REFRESH_TOKEN": false,
|
"CRON_REFRESH_TOKEN": false,
|
||||||
"PROVIDER_POOLS_FILE_PATH": "provider_pools.json"
|
"PROVIDER_POOLS_FILE_PATH": "provider_pools.json",
|
||||||
|
"MAX_ERROR_COUNT": 3
|
||||||
}
|
}
|
||||||
|
|
@ -85,7 +85,8 @@ export async function initializeConfig(args = process.argv.slice(2), configFileP
|
||||||
REQUEST_BASE_DELAY: 1000,
|
REQUEST_BASE_DELAY: 1000,
|
||||||
CRON_NEAR_MINUTES: 15,
|
CRON_NEAR_MINUTES: 15,
|
||||||
CRON_REFRESH_TOKEN: false,
|
CRON_REFRESH_TOKEN: false,
|
||||||
PROVIDER_POOLS_FILE_PATH: '' // 新增号池配置文件路径
|
PROVIDER_POOLS_FILE_PATH: '', // 新增号池配置文件路径
|
||||||
|
MAX_ERROR_COUNT: 3 // 提供商最大错误次数
|
||||||
};
|
};
|
||||||
console.log('[Config] Using default configuration.');
|
console.log('[Config] Using default configuration.');
|
||||||
}
|
}
|
||||||
|
|
@ -251,6 +252,13 @@ export async function initializeConfig(args = process.argv.slice(2), configFileP
|
||||||
} else {
|
} else {
|
||||||
console.warn(`[Config Warning] --provider-pools-file flag requires a value.`);
|
console.warn(`[Config Warning] --provider-pools-file flag requires a value.`);
|
||||||
}
|
}
|
||||||
|
} else if (args[i] === '--max-error-count') {
|
||||||
|
if (i + 1 < args.length) {
|
||||||
|
currentConfig.MAX_ERROR_COUNT = parseInt(args[i + 1], 10);
|
||||||
|
i++;
|
||||||
|
} else {
|
||||||
|
console.warn(`[Config Warning] --max-error-count flag requires a value.`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -491,7 +491,7 @@ export class QwenApiService {
|
||||||
const maxRetries = (this.config && this.config.REQUEST_MAX_RETRIES) || 3;
|
const maxRetries = (this.config && this.config.REQUEST_MAX_RETRIES) || 3;
|
||||||
const baseDelay = (this.config && this.config.REQUEST_BASE_DELAY) || 1000;
|
const baseDelay = (this.config && this.config.REQUEST_BASE_DELAY) || 1000;
|
||||||
|
|
||||||
const version = "0.0.9";
|
const version = "0.2.1";
|
||||||
const userAgent = `QwenCode/${version} (${process.platform}; ${process.arch})`;
|
const userAgent = `QwenCode/${version} (${process.platform}; ${process.arch})`;
|
||||||
console.log(`[QwenApiService] User-Agent: ${userAgent}`);
|
console.log(`[QwenApiService] User-Agent: ${userAgent}`);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,10 @@ let providerPoolManager = null;
|
||||||
*/
|
*/
|
||||||
export async function initApiService(config) {
|
export async function initApiService(config) {
|
||||||
if (config.providerPools && Object.keys(config.providerPools).length > 0) {
|
if (config.providerPools && Object.keys(config.providerPools).length > 0) {
|
||||||
providerPoolManager = new ProviderPoolManager(config.providerPools, { globalConfig: config });
|
providerPoolManager = new ProviderPoolManager(config.providerPools, {
|
||||||
|
globalConfig: config,
|
||||||
|
maxErrorCount: config.MAX_ERROR_COUNT || 3
|
||||||
|
});
|
||||||
console.log('[Initialization] ProviderPoolManager initialized with configured pools.');
|
console.log('[Initialization] ProviderPoolManager initialized with configured pools.');
|
||||||
// 健康检查将在服务器完全启动后执行
|
// 健康检查将在服务器完全启动后执行
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ import multer from 'multer';
|
||||||
import crypto from 'crypto';
|
import crypto from 'crypto';
|
||||||
import { getRequestBody } from './common.js';
|
import { getRequestBody } from './common.js';
|
||||||
import { CONFIG } from './config-manager.js';
|
import { CONFIG } from './config-manager.js';
|
||||||
|
import { serviceInstances } from './adapter.js';
|
||||||
|
import { initApiService } from './service-manager.js';
|
||||||
|
|
||||||
// Token存储在内存中(生产环境建议使用Redis)
|
// Token存储在内存中(生产环境建议使用Redis)
|
||||||
const tokenStore = new Map();
|
const tokenStore = new Map();
|
||||||
|
|
@ -252,6 +254,36 @@ export async function serveStaticFiles(pathParam, res) {
|
||||||
* @param {Object} providerPoolManager - The provider pool manager instance
|
* @param {Object} providerPoolManager - The provider pool manager instance
|
||||||
* @returns {Promise<boolean>} - True if the request was handled by UI API
|
* @returns {Promise<boolean>} - True if the request was handled by UI API
|
||||||
*/
|
*/
|
||||||
|
/**
|
||||||
|
* 重载配置文件
|
||||||
|
* 动态导入config-manager并重新初始化配置
|
||||||
|
* @returns {Promise<Object>} 返回重载后的配置对象
|
||||||
|
*/
|
||||||
|
async function reloadConfig() {
|
||||||
|
try {
|
||||||
|
// Import config manager dynamically
|
||||||
|
const { initializeConfig } = await import('./config-manager.js');
|
||||||
|
|
||||||
|
// Reload main config
|
||||||
|
const newConfig = await initializeConfig(process.argv.slice(2), 'config.json');
|
||||||
|
|
||||||
|
// Update global CONFIG
|
||||||
|
Object.assign(CONFIG, newConfig);
|
||||||
|
console.log('[UI API] Configuration reloaded:');
|
||||||
|
|
||||||
|
// Update initApiService - 清空并重新初始化服务实例
|
||||||
|
Object.keys(serviceInstances).forEach(key => delete serviceInstances[key]);
|
||||||
|
initApiService(CONFIG);
|
||||||
|
|
||||||
|
console.log('[UI API] Configuration reloaded successfully');
|
||||||
|
|
||||||
|
return newConfig;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[UI API] Failed to reload configuration:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function handleUIApiRequests(method, pathParam, req, res, currentConfig, providerPoolManager) {
|
export async function handleUIApiRequests(method, pathParam, req, res, currentConfig, providerPoolManager) {
|
||||||
// 处理登录接口
|
// 处理登录接口
|
||||||
if (method === 'POST' && pathParam === '/api/login') {
|
if (method === 'POST' && pathParam === '/api/login') {
|
||||||
|
|
@ -407,6 +439,7 @@ export async function handleUIApiRequests(method, pathParam, req, res, currentCo
|
||||||
if (newConfig.CRON_NEAR_MINUTES !== undefined) currentConfig.CRON_NEAR_MINUTES = newConfig.CRON_NEAR_MINUTES;
|
if (newConfig.CRON_NEAR_MINUTES !== undefined) currentConfig.CRON_NEAR_MINUTES = newConfig.CRON_NEAR_MINUTES;
|
||||||
if (newConfig.CRON_REFRESH_TOKEN !== undefined) currentConfig.CRON_REFRESH_TOKEN = newConfig.CRON_REFRESH_TOKEN;
|
if (newConfig.CRON_REFRESH_TOKEN !== undefined) currentConfig.CRON_REFRESH_TOKEN = newConfig.CRON_REFRESH_TOKEN;
|
||||||
if (newConfig.PROVIDER_POOLS_FILE_PATH !== undefined) currentConfig.PROVIDER_POOLS_FILE_PATH = newConfig.PROVIDER_POOLS_FILE_PATH;
|
if (newConfig.PROVIDER_POOLS_FILE_PATH !== undefined) currentConfig.PROVIDER_POOLS_FILE_PATH = newConfig.PROVIDER_POOLS_FILE_PATH;
|
||||||
|
if (newConfig.MAX_ERROR_COUNT !== undefined) currentConfig.MAX_ERROR_COUNT = newConfig.MAX_ERROR_COUNT;
|
||||||
|
|
||||||
// Handle system prompt update
|
// Handle system prompt update
|
||||||
if (newConfig.systemPrompt !== undefined) {
|
if (newConfig.systemPrompt !== undefined) {
|
||||||
|
|
@ -414,7 +447,7 @@ export async function handleUIApiRequests(method, pathParam, req, res, currentCo
|
||||||
try {
|
try {
|
||||||
const relativePath = path.relative(process.cwd(), promptPath);
|
const relativePath = path.relative(process.cwd(), promptPath);
|
||||||
writeFileSync(promptPath, newConfig.systemPrompt, 'utf-8');
|
writeFileSync(promptPath, newConfig.systemPrompt, 'utf-8');
|
||||||
|
|
||||||
// 广播更新事件
|
// 广播更新事件
|
||||||
broadcastEvent('config_update', {
|
broadcastEvent('config_update', {
|
||||||
action: 'update',
|
action: 'update',
|
||||||
|
|
@ -457,7 +490,8 @@ export async function handleUIApiRequests(method, pathParam, req, res, currentCo
|
||||||
REQUEST_BASE_DELAY: currentConfig.REQUEST_BASE_DELAY,
|
REQUEST_BASE_DELAY: currentConfig.REQUEST_BASE_DELAY,
|
||||||
CRON_NEAR_MINUTES: currentConfig.CRON_NEAR_MINUTES,
|
CRON_NEAR_MINUTES: currentConfig.CRON_NEAR_MINUTES,
|
||||||
CRON_REFRESH_TOKEN: currentConfig.CRON_REFRESH_TOKEN,
|
CRON_REFRESH_TOKEN: currentConfig.CRON_REFRESH_TOKEN,
|
||||||
PROVIDER_POOLS_FILE_PATH: currentConfig.PROVIDER_POOLS_FILE_PATH
|
PROVIDER_POOLS_FILE_PATH: currentConfig.PROVIDER_POOLS_FILE_PATH,
|
||||||
|
MAX_ERROR_COUNT: currentConfig.MAX_ERROR_COUNT
|
||||||
};
|
};
|
||||||
|
|
||||||
writeFileSync(configPath, JSON.stringify(configToSave, null, 2), 'utf-8');
|
writeFileSync(configPath, JSON.stringify(configToSave, null, 2), 'utf-8');
|
||||||
|
|
@ -616,9 +650,6 @@ export async function handleUIApiRequests(method, pathParam, req, res, currentCo
|
||||||
providerPoolManager.initializeProviderStatus();
|
providerPoolManager.initializeProviderStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update CONFIG cache to maintain consistency
|
|
||||||
CONFIG.providerPools = providerPools;
|
|
||||||
|
|
||||||
// 广播更新事件
|
// 广播更新事件
|
||||||
broadcastEvent('config_update', {
|
broadcastEvent('config_update', {
|
||||||
action: 'add',
|
action: 'add',
|
||||||
|
|
@ -716,9 +747,6 @@ export async function handleUIApiRequests(method, pathParam, req, res, currentCo
|
||||||
providerPoolManager.initializeProviderStatus();
|
providerPoolManager.initializeProviderStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update CONFIG cache to maintain consistency
|
|
||||||
CONFIG.providerPools = providerPools;
|
|
||||||
|
|
||||||
// 广播更新事件
|
// 广播更新事件
|
||||||
broadcastEvent('config_update', {
|
broadcastEvent('config_update', {
|
||||||
action: 'update',
|
action: 'update',
|
||||||
|
|
@ -791,9 +819,6 @@ export async function handleUIApiRequests(method, pathParam, req, res, currentCo
|
||||||
providerPoolManager.initializeProviderStatus();
|
providerPoolManager.initializeProviderStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update CONFIG cache to maintain consistency
|
|
||||||
CONFIG.providerPools = providerPools;
|
|
||||||
|
|
||||||
// 广播更新事件
|
// 广播更新事件
|
||||||
broadcastEvent('config_update', {
|
broadcastEvent('config_update', {
|
||||||
action: 'delete',
|
action: 'delete',
|
||||||
|
|
@ -870,9 +895,6 @@ export async function handleUIApiRequests(method, pathParam, req, res, currentCo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update CONFIG cache to maintain consistency
|
|
||||||
CONFIG.providerPools = providerPools;
|
|
||||||
|
|
||||||
// 广播更新事件
|
// 广播更新事件
|
||||||
broadcastEvent('config_update', {
|
broadcastEvent('config_update', {
|
||||||
action: action,
|
action: action,
|
||||||
|
|
@ -1065,14 +1087,8 @@ export async function handleUIApiRequests(method, pathParam, req, res, currentCo
|
||||||
// Reload configuration files
|
// Reload configuration files
|
||||||
if (method === 'POST' && pathParam === '/api/reload-config') {
|
if (method === 'POST' && pathParam === '/api/reload-config') {
|
||||||
try {
|
try {
|
||||||
// Import config manager dynamically
|
// 调用重载配置函数
|
||||||
const { initializeConfig } = await import('./config-manager.js');
|
const newConfig = await reloadConfig();
|
||||||
|
|
||||||
// Reload main config
|
|
||||||
const newConfig = await initializeConfig(process.argv.slice(2), 'config.json');
|
|
||||||
|
|
||||||
// Update global CONFIG
|
|
||||||
Object.assign(CONFIG, newConfig);
|
|
||||||
|
|
||||||
// 广播更新事件
|
// 广播更新事件
|
||||||
broadcastEvent('config_update', {
|
broadcastEvent('config_update', {
|
||||||
|
|
@ -1110,9 +1126,8 @@ export async function handleUIApiRequests(method, pathParam, req, res, currentCo
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize UI management features
|
* Initialize UI management features
|
||||||
* @param {Object} config - The server configuration
|
|
||||||
*/
|
*/
|
||||||
export function initializeUIManagement(config) {
|
export function initializeUIManagement() {
|
||||||
// Initialize log broadcasting for UI
|
// Initialize log broadcasting for UI
|
||||||
if (!global.eventClients) {
|
if (!global.eventClients) {
|
||||||
global.eventClients = [];
|
global.eventClients = [];
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,7 @@ async function loadConfiguration() {
|
||||||
const cronNearMinutesEl = document.getElementById('cronNearMinutes');
|
const cronNearMinutesEl = document.getElementById('cronNearMinutes');
|
||||||
const cronRefreshTokenEl = document.getElementById('cronRefreshToken');
|
const cronRefreshTokenEl = document.getElementById('cronRefreshToken');
|
||||||
const providerPoolsFilePathEl = document.getElementById('providerPoolsFilePath');
|
const providerPoolsFilePathEl = document.getElementById('providerPoolsFilePath');
|
||||||
|
const maxErrorCountEl = document.getElementById('maxErrorCount');
|
||||||
|
|
||||||
if (systemPromptFilePathEl) systemPromptFilePathEl.value = data.SYSTEM_PROMPT_FILE_PATH || 'input_system_prompt.txt';
|
if (systemPromptFilePathEl) systemPromptFilePathEl.value = data.SYSTEM_PROMPT_FILE_PATH || 'input_system_prompt.txt';
|
||||||
if (systemPromptModeEl) systemPromptModeEl.value = data.SYSTEM_PROMPT_MODE || 'append';
|
if (systemPromptModeEl) systemPromptModeEl.value = data.SYSTEM_PROMPT_MODE || 'append';
|
||||||
|
|
@ -85,6 +86,7 @@ async function loadConfiguration() {
|
||||||
if (cronNearMinutesEl) cronNearMinutesEl.value = data.CRON_NEAR_MINUTES || 1;
|
if (cronNearMinutesEl) cronNearMinutesEl.value = data.CRON_NEAR_MINUTES || 1;
|
||||||
if (cronRefreshTokenEl) cronRefreshTokenEl.checked = data.CRON_REFRESH_TOKEN || false;
|
if (cronRefreshTokenEl) cronRefreshTokenEl.checked = data.CRON_REFRESH_TOKEN || false;
|
||||||
if (providerPoolsFilePathEl) providerPoolsFilePathEl.value = data.PROVIDER_POOLS_FILE_PATH || '';
|
if (providerPoolsFilePathEl) providerPoolsFilePathEl.value = data.PROVIDER_POOLS_FILE_PATH || '';
|
||||||
|
if (maxErrorCountEl) maxErrorCountEl.value = data.MAX_ERROR_COUNT || 3;
|
||||||
|
|
||||||
// 触发提供商配置显示
|
// 触发提供商配置显示
|
||||||
handleProviderChange();
|
handleProviderChange();
|
||||||
|
|
@ -188,10 +190,11 @@ async function saveConfiguration() {
|
||||||
config.CRON_NEAR_MINUTES = parseInt(document.getElementById('cronNearMinutes')?.value || 1);
|
config.CRON_NEAR_MINUTES = parseInt(document.getElementById('cronNearMinutes')?.value || 1);
|
||||||
config.CRON_REFRESH_TOKEN = document.getElementById('cronRefreshToken')?.checked || false;
|
config.CRON_REFRESH_TOKEN = document.getElementById('cronRefreshToken')?.checked || false;
|
||||||
config.PROVIDER_POOLS_FILE_PATH = document.getElementById('providerPoolsFilePath')?.value || '';
|
config.PROVIDER_POOLS_FILE_PATH = document.getElementById('providerPoolsFilePath')?.value || '';
|
||||||
|
config.MAX_ERROR_COUNT = parseInt(document.getElementById('maxErrorCount')?.value || 3);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await window.apiClient.post('/config', config);
|
await window.apiClient.post('/config', config);
|
||||||
|
await window.apiClient.post('/reload-config');
|
||||||
showToast('配置已保存', 'success');
|
showToast('配置已保存', 'success');
|
||||||
|
|
||||||
// 检查当前是否在提供商池管理页面,如果是则刷新数据
|
// 检查当前是否在提供商池管理页面,如果是则刷新数据
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,10 @@ class FileUploadHandler {
|
||||||
const button = event.target.closest('.upload-btn');
|
const button = event.target.closest('.upload-btn');
|
||||||
const targetInputId = button.getAttribute('data-target');
|
const targetInputId = button.getAttribute('data-target');
|
||||||
if (targetInputId) {
|
if (targetInputId) {
|
||||||
this.handleFileUpload(button, targetInputId);
|
// 尝试从模态框获取 providerType
|
||||||
|
const modal = button.closest('.provider-modal');
|
||||||
|
const providerType = modal ? modal.getAttribute('data-provider-type') : null;
|
||||||
|
this.handleFileUpload(button, targetInputId, providerType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -61,8 +64,9 @@ class FileUploadHandler {
|
||||||
* 处理文件上传
|
* 处理文件上传
|
||||||
* @param {HTMLElement} button - 上传按钮元素
|
* @param {HTMLElement} button - 上传按钮元素
|
||||||
* @param {string} targetInputId - 目标输入框ID
|
* @param {string} targetInputId - 目标输入框ID
|
||||||
|
* @param {string} providerType - 提供商类型
|
||||||
*/
|
*/
|
||||||
async handleFileUpload(button, targetInputId) {
|
async handleFileUpload(button, targetInputId, providerType) {
|
||||||
// 创建隐藏的文件输入元素
|
// 创建隐藏的文件输入元素
|
||||||
const fileInput = this.createFileInput();
|
const fileInput = this.createFileInput();
|
||||||
|
|
||||||
|
|
@ -73,7 +77,7 @@ class FileUploadHandler {
|
||||||
if (file) {
|
if (file) {
|
||||||
// 只有文件被实际选择后才显示加载状态并上传
|
// 只有文件被实际选择后才显示加载状态并上传
|
||||||
this.setButtonLoading(button, true);
|
this.setButtonLoading(button, true);
|
||||||
await this.uploadFile(file, targetInputId, button);
|
await this.uploadFile(file, targetInputId, button, providerType);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清理临时文件输入元素
|
// 清理临时文件输入元素
|
||||||
|
|
@ -102,8 +106,9 @@ class FileUploadHandler {
|
||||||
* @param {File} file - 要上传的文件
|
* @param {File} file - 要上传的文件
|
||||||
* @param {string} targetInputId - 目标输入框ID
|
* @param {string} targetInputId - 目标输入框ID
|
||||||
* @param {HTMLElement} button - 上传按钮
|
* @param {HTMLElement} button - 上传按钮
|
||||||
|
* @param {string} providerType - 提供商类型
|
||||||
*/
|
*/
|
||||||
async uploadFile(file, targetInputId, button) {
|
async uploadFile(file, targetInputId, button, providerType) {
|
||||||
try {
|
try {
|
||||||
// 验证文件类型
|
// 验证文件类型
|
||||||
if (!this.validateFileType(file)) {
|
if (!this.validateFileType(file)) {
|
||||||
|
|
@ -119,10 +124,13 @@ class FileUploadHandler {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 使用传入的 providerType 或回退到 currentProvider
|
||||||
|
const provider = providerType ? this.getProviderKey(providerType) : this.currentProvider;
|
||||||
|
|
||||||
// 创建 FormData
|
// 创建 FormData
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('file', file);
|
formData.append('file', file);
|
||||||
formData.append('provider', this.currentProvider);
|
formData.append('provider', provider);
|
||||||
formData.append('targetInputId', targetInputId);
|
formData.append('targetInputId', targetInputId);
|
||||||
|
|
||||||
// 使用封装接口发送上传请求
|
// 使用封装接口发送上传请求
|
||||||
|
|
|
||||||
|
|
@ -107,8 +107,9 @@ function addModalEventListeners(modal) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
const targetInputId = button.getAttribute('data-target');
|
const targetInputId = button.getAttribute('data-target');
|
||||||
|
const providerType = modal.getAttribute('data-provider-type');
|
||||||
if (targetInputId && window.fileUploadHandler) {
|
if (targetInputId && window.fileUploadHandler) {
|
||||||
window.fileUploadHandler.handleFileUpload(button, targetInputId);
|
window.fileUploadHandler.handleFileUpload(button, targetInputId, providerType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -564,6 +565,7 @@ async function saveProvider(uuid, event) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await window.apiClient.put(`/providers/${encodeURIComponent(providerType)}/${uuid}`, { providerConfig });
|
await window.apiClient.put(`/providers/${encodeURIComponent(providerType)}/${uuid}`, { providerConfig });
|
||||||
|
await window.apiClient.post('/reload-config');
|
||||||
showToast('提供商配置更新成功', 'success');
|
showToast('提供商配置更新成功', 'success');
|
||||||
// 重新获取该提供商类型的最新配置
|
// 重新获取该提供商类型的最新配置
|
||||||
await refreshProviderConfig(providerType);
|
await refreshProviderConfig(providerType);
|
||||||
|
|
@ -590,6 +592,7 @@ async function deleteProvider(uuid, event) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await window.apiClient.delete(`/providers/${encodeURIComponent(providerType)}/${uuid}`);
|
await window.apiClient.delete(`/providers/${encodeURIComponent(providerType)}/${uuid}`);
|
||||||
|
await window.apiClient.post('/reload-config');
|
||||||
showToast('提供商配置删除成功', 'success');
|
showToast('提供商配置删除成功', 'success');
|
||||||
// 重新获取最新配置
|
// 重新获取最新配置
|
||||||
await refreshProviderConfig(providerType);
|
await refreshProviderConfig(providerType);
|
||||||
|
|
@ -870,6 +873,7 @@ async function addProvider(providerType) {
|
||||||
providerType,
|
providerType,
|
||||||
providerConfig
|
providerConfig
|
||||||
});
|
});
|
||||||
|
await window.apiClient.post('/reload-config');
|
||||||
showToast('提供商配置添加成功', 'success');
|
showToast('提供商配置添加成功', 'success');
|
||||||
// 移除添加表单
|
// 移除添加表单
|
||||||
const form = document.querySelector('.add-provider-form');
|
const form = document.querySelector('.add-provider-form');
|
||||||
|
|
@ -909,6 +913,7 @@ async function toggleProviderStatus(uuid, event) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await window.apiClient.post(`/providers/${encodeURIComponent(providerType)}/${uuid}/${action}`, { action });
|
await window.apiClient.post(`/providers/${encodeURIComponent(providerType)}/${uuid}/${action}`, { action });
|
||||||
|
await window.apiClient.post('/reload-config');
|
||||||
showToast(`提供商${isCurrentlyDisabled ? '启用' : '禁用'}成功`, 'success');
|
showToast(`提供商${isCurrentlyDisabled ? '启用' : '禁用'}成功`, 'success');
|
||||||
// 重新获取该提供商类型的最新配置
|
// 重新获取该提供商类型的最新配置
|
||||||
await refreshProviderConfig(providerType);
|
await refreshProviderConfig(providerType);
|
||||||
|
|
|
||||||
|
|
@ -607,6 +607,12 @@
|
||||||
<small class="form-text">配置了提供商池后,可在提供商池管理中查看详细信息</small>
|
<small class="form-text">配置了提供商池后,可在提供商池管理中查看详细信息</small>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group pool-section">
|
||||||
|
<label for="maxErrorCount">提供商最大错误次数</label>
|
||||||
|
<input type="number" id="maxErrorCount" class="form-control" value="3" min="1" max="10" placeholder="默认: 3">
|
||||||
|
<small class="form-text">提供商连续错误达到此次数后将被标记为不健康,默认为 3 次</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 系统提示配置移到最下面 -->
|
<!-- 系统提示配置移到最下面 -->
|
||||||
<div class="form-group system-prompt-section">
|
<div class="form-group system-prompt-section">
|
||||||
<label for="systemPrompt">系统提示</label>
|
<label for="systemPrompt">系统提示</label>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue