From bd8f03b68e6053b769ea99623d895d544efceb9d Mon Sep 17 00:00:00 2001 From: leonai <731962175@qq.com> Date: Tue, 13 Jan 2026 19:00:00 +0800 Subject: [PATCH] =?UTF-8?q?feat(config):=20=E6=B7=BB=E5=8A=A0=E5=87=AD?= =?UTF-8?q?=E8=AF=81=E5=88=87=E6=8D=A2=E6=9C=80=E5=A4=A7=E9=87=8D=E8=AF=95?= =?UTF-8?q?=E6=AC=A1=E6=95=B0=E9=85=8D=E7=BD=AE=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 新增 CREDENTIAL_SWITCH_MAX_RETRIES 配置项,默认值为 5 2. 在 config-manager.js 中添加配置初始化 3. 在 config-api.js 中支持配置的读取和更新 4. 在 common.js 中使用该配置控制凭证切换重试次数 5. 在前端配置页面添加对应的输入控件 --- src/core/config-manager.js | 1 + src/ui-modules/config-api.js | 2 ++ src/utils/common.js | 8 +++++++- static/app/config-manager.js | 6 ++++++ static/components/section-config.html | 8 ++++++++ 5 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/core/config-manager.js b/src/core/config-manager.js index 0b6d460..cc68110 100644 --- a/src/core/config-manager.js +++ b/src/core/config-manager.js @@ -75,6 +75,7 @@ export async function initializeConfig(args = process.argv.slice(2), configFileP PROMPT_LOG_MODE: "none", REQUEST_MAX_RETRIES: 3, REQUEST_BASE_DELAY: 1000, + CREDENTIAL_SWITCH_MAX_RETRIES: 5, // 坏凭证切换最大重试次数(用于认证错误后切换凭证) CRON_NEAR_MINUTES: 15, CRON_REFRESH_TOKEN: false, PROVIDER_POOLS_FILE_PATH: null, // 新增号池配置文件路径 diff --git a/src/ui-modules/config-api.js b/src/ui-modules/config-api.js index 4d36c23..1ddfdfd 100644 --- a/src/ui-modules/config-api.js +++ b/src/ui-modules/config-api.js @@ -83,6 +83,7 @@ export async function handleUpdateConfig(req, res, currentConfig) { if (newConfig.PROMPT_LOG_MODE !== undefined) currentConfig.PROMPT_LOG_MODE = newConfig.PROMPT_LOG_MODE; if (newConfig.REQUEST_MAX_RETRIES !== undefined) currentConfig.REQUEST_MAX_RETRIES = newConfig.REQUEST_MAX_RETRIES; if (newConfig.REQUEST_BASE_DELAY !== undefined) currentConfig.REQUEST_BASE_DELAY = newConfig.REQUEST_BASE_DELAY; + if (newConfig.CREDENTIAL_SWITCH_MAX_RETRIES !== undefined) currentConfig.CREDENTIAL_SWITCH_MAX_RETRIES = newConfig.CREDENTIAL_SWITCH_MAX_RETRIES; 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.PROVIDER_POOLS_FILE_PATH !== undefined) currentConfig.PROVIDER_POOLS_FILE_PATH = newConfig.PROVIDER_POOLS_FILE_PATH; @@ -131,6 +132,7 @@ export async function handleUpdateConfig(req, res, currentConfig) { PROMPT_LOG_MODE: currentConfig.PROMPT_LOG_MODE, REQUEST_MAX_RETRIES: currentConfig.REQUEST_MAX_RETRIES, REQUEST_BASE_DELAY: currentConfig.REQUEST_BASE_DELAY, + CREDENTIAL_SWITCH_MAX_RETRIES: currentConfig.CREDENTIAL_SWITCH_MAX_RETRIES, CRON_NEAR_MINUTES: currentConfig.CRON_NEAR_MINUTES, CRON_REFRESH_TOKEN: currentConfig.CRON_REFRESH_TOKEN, PROVIDER_POOLS_FILE_PATH: currentConfig.PROVIDER_POOLS_FILE_PATH, diff --git a/src/utils/common.js b/src/utils/common.js index aaa67fb..d3acc92 100644 --- a/src/utils/common.js +++ b/src/utils/common.js @@ -627,7 +627,13 @@ export async function handleContentGenerationRequest(req, res, service, endpoint // 5. Call the appropriate stream or unary handler, passing the provider info. // 创建重试上下文,包含 CONFIG 以便在认证错误时切换凭证重试 - const retryContext = providerPoolManager ? { CONFIG, currentRetry: 0, maxRetries: 2 } : null; + // 凭证切换重试次数(默认 5),可在配置中自定义更大的值 + // 注意:这与底层的 429/5xx 重试(REQUEST_MAX_RETRIES)是不同层次的重试机制 + // - 底层重试:同一凭证遇到 429/5xx 时的重试 + // - 凭证切换重试:凭证被标记不健康后切换到其他凭证 + // 当没有不同的健康凭证可用时,重试会自动停止 + const credentialSwitchMaxRetries = CONFIG.CREDENTIAL_SWITCH_MAX_RETRIES || 5; + const retryContext = providerPoolManager ? { CONFIG, currentRetry: 0, maxRetries: credentialSwitchMaxRetries } : null; if (isStream) { await handleStreamRequest(res, service, model, processedRequestBody, fromProvider, toProvider, CONFIG.PROMPT_LOG_MODE, PROMPT_LOG_FILENAME, providerPoolManager, actualUuid, actualCustomName, retryContext); diff --git a/static/app/config-manager.js b/static/app/config-manager.js index 4371c7f..a669f85 100644 --- a/static/app/config-manager.js +++ b/static/app/config-manager.js @@ -90,6 +90,11 @@ async function loadConfiguration() { if (promptLogModeEl) promptLogModeEl.value = data.PROMPT_LOG_MODE || 'none'; if (requestMaxRetriesEl) requestMaxRetriesEl.value = data.REQUEST_MAX_RETRIES || 3; if (requestBaseDelayEl) requestBaseDelayEl.value = data.REQUEST_BASE_DELAY || 1000; + + // 坏凭证切换最大重试次数 + const credentialSwitchMaxRetriesEl = document.getElementById('credentialSwitchMaxRetries'); + if (credentialSwitchMaxRetriesEl) credentialSwitchMaxRetriesEl.value = data.CREDENTIAL_SWITCH_MAX_RETRIES || 5; + if (cronNearMinutesEl) cronNearMinutesEl.value = data.CRON_NEAR_MINUTES || 1; if (cronRefreshTokenEl) cronRefreshTokenEl.checked = data.CRON_REFRESH_TOKEN || false; if (providerPoolsFilePathEl) providerPoolsFilePathEl.value = data.PROVIDER_POOLS_FILE_PATH; @@ -187,6 +192,7 @@ async function saveConfiguration() { config.PROMPT_LOG_MODE = document.getElementById('promptLogMode')?.value || ''; config.REQUEST_MAX_RETRIES = parseInt(document.getElementById('requestMaxRetries')?.value || 3); config.REQUEST_BASE_DELAY = parseInt(document.getElementById('requestBaseDelay')?.value || 1000); + config.CREDENTIAL_SWITCH_MAX_RETRIES = parseInt(document.getElementById('credentialSwitchMaxRetries')?.value || 5); config.CRON_NEAR_MINUTES = parseInt(document.getElementById('cronNearMinutes')?.value || 1); config.CRON_REFRESH_TOKEN = document.getElementById('cronRefreshToken')?.checked || false; config.PROVIDER_POOLS_FILE_PATH = document.getElementById('providerPoolsFilePath')?.value || ''; diff --git a/static/components/section-config.html b/static/components/section-config.html index 12a7361..c51e6bb 100644 --- a/static/components/section-config.html +++ b/static/components/section-config.html @@ -162,6 +162,14 @@ +