From ee7e837d1fef63e4dea70c8a364d97479564e9e2 Mon Sep 17 00:00:00 2001
From: Zyy <0x@ainz.cc>
Date: Fri, 6 Nov 2026 23:09:29 +0800
Subject: [PATCH] feat(scheduling): split queue timeouts and allow account
fallback (#625)
Co-authored-by: Zyy
---
.../gateway-admin/src/model/settings.rs | 7 +-
.../tests/use_case/accounts/mod.rs | 4 +-
.../tests/use_case/freeze_recovery.rs | 4 +-
.../tests/use_case/observability/mod.rs | 4 +-
.../gateway-admin/tests/use_case/settings.rs | 4 +-
.../crates/gateway-api/src/admin/settings.rs | 15 +-
backend/crates/gateway-api/tests/admin/mod.rs | 8 +-
.../gateway-api/tests/admin/settings.rs | 12 +-
.../crates/gateway-api/tests/openai/models.rs | 4 +-
.../gateway-core/src/account/selection.rs | 43 ++++++
.../crates/gateway-core/src/concurrency.rs | 2 +-
.../gateway-core/src/engine/coordinator.rs | 6 -
.../src/engine/execution/scope.rs | 2 -
.../src/engine/execution/service.rs | 10 +-
backend/crates/gateway-core/src/engine/mod.rs | 2 +-
.../gateway-core/src/settings/compiled.rs | 12 +-
.../gateway-core/src/settings/values.rs | 27 +++-
.../crates/gateway-core/tests/concurrency.rs | 11 +-
.../tests/engine/execution/mod.rs | 23 +--
.../sdk/src/call/services/settings.rs | 12 +-
.../crates/gateway-store/src/admin_adapter.rs | 4 +-
.../src/postgres/runtime_settings.rs | 20 ++-
.../gateway-store/src/postgres/snapshot.rs | 10 +-
.../tests/postgres/runtime_settings.rs | 30 ++--
.../openai/src/credential/selector.rs | 60 ++++++--
.../contract/scheduling_projection.rs | 135 ++++++++++++++++++
.../tests/provider/contract/capacity.rs | 10 +-
.../providers/xai/src/credential/selector.rs | 35 ++++-
.../xai/tests/credential/selector.rs | 44 ++++++
backend/migrations/.frozen-sha256 | 1 +
backend/migrations/0039_queue_timeouts.sql | 10 ++
docs/api.md | 15 +-
docs/architecture.md | 4 +-
frontend/src/api/modules/settings/index.ts | 4 +-
frontend/src/i18n/locales/en/settings.json | 23 ++-
.../src/i18n/locales/zh-Hans/settings.json | 23 ++-
.../src/i18n/locales/zh-Hant/settings.json | 23 ++-
.../components/ConcurrencyPolicyCard.vue | 85 +++++++----
.../settings/components/TokenRefreshCard.vue | 5 +-
.../AccountErrorProtectionCard.vue | 4 +-
.../settings/composables/useSettingsForm.ts | 22 ++-
.../composables/useSettingsFormRules.ts | 3 +-
frontend/src/views/settings/index.vue | 7 +-
43 files changed, 622 insertions(+), 167 deletions(-)
create mode 100644 backend/migrations/0039_queue_timeouts.sql
diff --git a/backend/crates/gateway-admin/src/model/settings.rs b/backend/crates/gateway-admin/src/model/settings.rs
index a86629a0b..257111015 100644
--- a/backend/crates/gateway-admin/src/model/settings.rs
+++ b/backend/crates/gateway-admin/src/model/settings.rs
@@ -53,7 +53,9 @@ pub struct RuntimeSettingsValues {
pub max_concurrent_per_account: u32,
pub max_waiting_per_key: u32,
pub max_waiting_per_account: u32,
- pub concurrency_wait_timeout_seconds: u32,
+ pub key_queue_timeout_seconds: u32,
+ pub account_queue_timeout_seconds: u32,
+ pub switch_account_on_queue_failure: bool,
pub max_retry_after_seconds: u32,
pub openai_guardian_reserved_concurrency: u32,
pub openai_account_affinity: gateway_core::account::AccountAffinity,
@@ -201,7 +203,8 @@ impl RuntimeSettingsValues {
gateway_core::settings::validate_request_limits(
self.max_waiting_per_key,
self.max_waiting_per_account,
- self.concurrency_wait_timeout_seconds,
+ self.key_queue_timeout_seconds,
+ self.account_queue_timeout_seconds,
self.max_account_rotations,
self.openai_session_affinity_ttl_hours,
)?;
diff --git a/backend/crates/gateway-admin/tests/use_case/accounts/mod.rs b/backend/crates/gateway-admin/tests/use_case/accounts/mod.rs
index 8bd27b700..aeb538cfd 100644
--- a/backend/crates/gateway-admin/tests/use_case/accounts/mod.rs
+++ b/backend/crates/gateway-admin/tests/use_case/accounts/mod.rs
@@ -1166,7 +1166,9 @@ impl SettingsStore for StaticSettingsStore {
max_concurrent_per_account: 1,
max_waiting_per_key: 0,
max_waiting_per_account: 0,
- concurrency_wait_timeout_seconds: 30,
+ key_queue_timeout_seconds: 30,
+ account_queue_timeout_seconds: 30,
+ switch_account_on_queue_failure: false,
max_retry_after_seconds: 240,
openai_guardian_reserved_concurrency: 0,
openai_account_affinity: gateway_core::account::AccountAffinity::Relaxed,
diff --git a/backend/crates/gateway-admin/tests/use_case/freeze_recovery.rs b/backend/crates/gateway-admin/tests/use_case/freeze_recovery.rs
index a5d7a46d4..7565435f3 100644
--- a/backend/crates/gateway-admin/tests/use_case/freeze_recovery.rs
+++ b/backend/crates/gateway-admin/tests/use_case/freeze_recovery.rs
@@ -41,7 +41,9 @@ fn runtime_settings(enabled: bool, probe_enabled: bool) -> RuntimeSettings {
max_concurrent_per_account: 5,
max_waiting_per_key: 0,
max_waiting_per_account: 0,
- concurrency_wait_timeout_seconds: 30,
+ key_queue_timeout_seconds: 30,
+ account_queue_timeout_seconds: 30,
+ switch_account_on_queue_failure: false,
max_retry_after_seconds: 240,
openai_guardian_reserved_concurrency: 0,
openai_account_affinity: gateway_core::account::AccountAffinity::Relaxed,
diff --git a/backend/crates/gateway-admin/tests/use_case/observability/mod.rs b/backend/crates/gateway-admin/tests/use_case/observability/mod.rs
index 349803cd4..5e59e2561 100644
--- a/backend/crates/gateway-admin/tests/use_case/observability/mod.rs
+++ b/backend/crates/gateway-admin/tests/use_case/observability/mod.rs
@@ -1420,7 +1420,9 @@ impl SettingsStore for FixtureSettingsStore {
max_concurrent_per_account: self.max_concurrent_per_account,
max_waiting_per_key: 0,
max_waiting_per_account: 0,
- concurrency_wait_timeout_seconds: 30,
+ key_queue_timeout_seconds: 30,
+ account_queue_timeout_seconds: 30,
+ switch_account_on_queue_failure: false,
max_retry_after_seconds: 240,
openai_guardian_reserved_concurrency: 0,
openai_account_affinity: gateway_core::account::AccountAffinity::Relaxed,
diff --git a/backend/crates/gateway-admin/tests/use_case/settings.rs b/backend/crates/gateway-admin/tests/use_case/settings.rs
index c25ccc53f..a269f6886 100644
--- a/backend/crates/gateway-admin/tests/use_case/settings.rs
+++ b/backend/crates/gateway-admin/tests/use_case/settings.rs
@@ -333,7 +333,9 @@ async fn settings_should_reject_zero_refresh_margin_before_store_call() {
max_concurrent_per_account: 1,
max_waiting_per_key: 0,
max_waiting_per_account: 0,
- concurrency_wait_timeout_seconds: 30,
+ key_queue_timeout_seconds: 30,
+ account_queue_timeout_seconds: 30,
+ switch_account_on_queue_failure: false,
max_retry_after_seconds: 240,
openai_guardian_reserved_concurrency: 0,
openai_account_affinity: gateway_core::account::AccountAffinity::Relaxed,
diff --git a/backend/crates/gateway-api/src/admin/settings.rs b/backend/crates/gateway-api/src/admin/settings.rs
index 8cbb71109..32515b2a6 100644
--- a/backend/crates/gateway-api/src/admin/settings.rs
+++ b/backend/crates/gateway-api/src/admin/settings.rs
@@ -44,7 +44,9 @@ pub struct RuntimeSettingsFields {
pub max_concurrent_per_account: u64,
pub max_waiting_per_key: u32,
pub max_waiting_per_account: u32,
- pub concurrency_wait_timeout_seconds: u32,
+ pub key_queue_timeout_seconds: u32,
+ pub account_queue_timeout_seconds: u32,
+ pub switch_account_on_queue_failure: bool,
pub max_retry_after_seconds: u32,
pub openai_guardian_reserved_concurrency: u32,
pub openai_account_affinity: gateway_core::account::AccountAffinity,
@@ -751,7 +753,9 @@ impl RuntimeSettingsFields {
.map_err(|_| WireValidationError::new("settingsMaxConcurrencyOverflow"))?,
max_waiting_per_key: self.max_waiting_per_key,
max_waiting_per_account: self.max_waiting_per_account,
- concurrency_wait_timeout_seconds: self.concurrency_wait_timeout_seconds,
+ key_queue_timeout_seconds: self.key_queue_timeout_seconds,
+ account_queue_timeout_seconds: self.account_queue_timeout_seconds,
+ switch_account_on_queue_failure: self.switch_account_on_queue_failure,
max_retry_after_seconds: self.max_retry_after_seconds,
openai_guardian_reserved_concurrency: self.openai_guardian_reserved_concurrency,
openai_account_affinity: self.openai_account_affinity,
@@ -791,7 +795,9 @@ impl From for RuntimeSett
max_concurrent_per_account: u64::from(settings.max_concurrent_per_account),
max_waiting_per_key: settings.max_waiting_per_key,
max_waiting_per_account: settings.max_waiting_per_account,
- concurrency_wait_timeout_seconds: settings.concurrency_wait_timeout_seconds,
+ key_queue_timeout_seconds: settings.key_queue_timeout_seconds,
+ account_queue_timeout_seconds: settings.account_queue_timeout_seconds,
+ switch_account_on_queue_failure: settings.switch_account_on_queue_failure,
max_retry_after_seconds: settings.max_retry_after_seconds,
openai_guardian_reserved_concurrency: settings.openai_guardian_reserved_concurrency,
openai_account_affinity: settings.openai_account_affinity,
@@ -824,7 +830,8 @@ fn settings_validation_error(field: &'static str) -> WireValidationError {
"reset_credit_max_weekly_remaining_percent" => "resetCreditMaxWeeklyRemainingPercent",
"account_warmup_schedule_time" => "accountWarmupScheduleTime",
"audit_retention_days" => "auditRetentionDays",
- "concurrency_wait_timeout_seconds" => "concurrencyWaitTimeoutSeconds",
+ "key_queue_timeout_seconds" => "keyQueueTimeoutSeconds",
+ "account_queue_timeout_seconds" => "accountQueueTimeoutSeconds",
"max_account_rotations" => "maxAccountRotations",
"max_concurrent_per_account" => "maxConcurrentPerAccount",
"max_waiting_per_account" => "maxWaitingPerAccount",
diff --git a/backend/crates/gateway-api/tests/admin/mod.rs b/backend/crates/gateway-api/tests/admin/mod.rs
index 7a777ac6a..c2a5dbebb 100644
--- a/backend/crates/gateway-api/tests/admin/mod.rs
+++ b/backend/crates/gateway-api/tests/admin/mod.rs
@@ -650,7 +650,9 @@ impl SettingsStore for MemorySettingsStore {
max_concurrent_per_account: command.values.max_concurrent_per_account,
max_waiting_per_key: command.values.max_waiting_per_key,
max_waiting_per_account: command.values.max_waiting_per_account,
- concurrency_wait_timeout_seconds: command.values.concurrency_wait_timeout_seconds,
+ key_queue_timeout_seconds: command.values.key_queue_timeout_seconds,
+ account_queue_timeout_seconds: command.values.account_queue_timeout_seconds,
+ switch_account_on_queue_failure: command.values.switch_account_on_queue_failure,
max_retry_after_seconds: command.values.max_retry_after_seconds,
openai_guardian_reserved_concurrency: command
.values
@@ -1895,7 +1897,9 @@ fn test_runtime_settings() -> RuntimeSettings {
max_concurrent_per_account: 3,
max_waiting_per_key: 0,
max_waiting_per_account: 0,
- concurrency_wait_timeout_seconds: 30,
+ key_queue_timeout_seconds: 30,
+ account_queue_timeout_seconds: 30,
+ switch_account_on_queue_failure: false,
max_retry_after_seconds: 240,
openai_guardian_reserved_concurrency: 0,
openai_account_affinity: gateway_core::account::AccountAffinity::Relaxed,
diff --git a/backend/crates/gateway-api/tests/admin/settings.rs b/backend/crates/gateway-api/tests/admin/settings.rs
index dd101b184..49b526dfa 100644
--- a/backend/crates/gateway-api/tests/admin/settings.rs
+++ b/backend/crates/gateway-api/tests/admin/settings.rs
@@ -56,7 +56,9 @@ fn update_body() -> Value {
"openaiAccountAffinity": "relaxed",
"maxAccountRotations": 3,
"openaiSessionAffinityTtlHours": 24,
- "concurrencyWaitTimeoutSeconds": 30,
+ "keyQueueTimeoutSeconds": 30,
+ "accountQueueTimeoutSeconds": 30,
+ "switchAccountOnQueueFailure": false,
"maxRetryAfterSeconds": 240,
"responsesMaxDecompressedBodyBytes": 67108864,
"rotationStrategy": "round_robin",
@@ -659,8 +661,10 @@ fn concurrency_queue_fields_validate_bounds_and_accept_disabled_queues() {
for (field, invalid) in [
("maxWaitingPerKey", 1001),
("maxWaitingPerAccount", 1001),
- ("concurrencyWaitTimeoutSeconds", 0),
- ("concurrencyWaitTimeoutSeconds", 121),
+ ("keyQueueTimeoutSeconds", 0),
+ ("accountQueueTimeoutSeconds", 0),
+ ("keyQueueTimeoutSeconds", 121),
+ ("accountQueueTimeoutSeconds", 121),
] {
let mut body = update_body();
body[field] = json!(invalid);
@@ -670,7 +674,7 @@ fn concurrency_queue_fields_validate_bounds_and_accept_disabled_queues() {
let mut body = update_body();
body["maxWaitingPerKey"] = json!(0);
body["maxWaitingPerAccount"] = json!(1000);
- body["concurrencyWaitTimeoutSeconds"] = json!(120);
+ body["accountQueueTimeoutSeconds"] = json!(120);
serde_json::from_value::(body)
.unwrap()
.validate()
diff --git a/backend/crates/gateway-api/tests/openai/models.rs b/backend/crates/gateway-api/tests/openai/models.rs
index 183be4e02..cdf1731c6 100644
--- a/backend/crates/gateway-api/tests/openai/models.rs
+++ b/backend/crates/gateway-api/tests/openai/models.rs
@@ -208,7 +208,9 @@ async fn request_settings_reach_query_terminal_without_mutating_authenticated_ba
settings: Some(|settings| {
let mut runtime = serde_json::to_value(&settings.runtime).unwrap();
runtime["model_mappings"] = serde_json::json!({"model-a":"plugin-model"});
- runtime["concurrency_wait_timeout_seconds"] = serde_json::json!(30);
+ runtime["key_queue_timeout_seconds"] = serde_json::json!(30);
+ runtime["account_queue_timeout_seconds"] = serde_json::json!(30);
+ runtime["switch_account_on_queue_failure"] = serde_json::json!(false);
settings.runtime = serde_json::from_value(runtime).unwrap();
settings.fast_mode = FastMode::Disabled;
}),
diff --git a/backend/crates/gateway-core/src/account/selection.rs b/backend/crates/gateway-core/src/account/selection.rs
index 318dacce8..32fc34b12 100644
--- a/backend/crates/gateway-core/src/account/selection.rs
+++ b/backend/crates/gateway-core/src/account/selection.rs
@@ -6,6 +6,8 @@ use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::{Mutex, RwLock};
use std::time::{Duration, Instant, SystemTime};
+use futures::FutureExt;
+
use crate::concurrency::{CapacityWait, ConcurrencyQueuePolicy, QueueRejection};
use crate::identity::ProviderKind;
@@ -92,6 +94,7 @@ pub struct AccountSelectionPolicy {
queue_policy: ConcurrencyQueuePolicy,
openai_guardian_reserved_concurrency: u32,
openai_account_affinity: AccountAffinity,
+ switch_account_on_queue_failure: bool,
max_account_rotations: u32,
openai_session_affinity_ttl: Duration,
}
@@ -108,6 +111,7 @@ impl AccountSelectionPolicy {
max_concurrent_per_account: max_concurrent_per_account.into(),
openai_guardian_reserved_concurrency: 0,
openai_account_affinity: AccountAffinity::default(),
+ switch_account_on_queue_failure: false,
max_account_rotations: 3,
openai_session_affinity_ttl: Duration::from_secs(24 * 3600),
queue_policy: ConcurrencyQueuePolicy {
@@ -128,6 +132,45 @@ impl AccountSelectionPolicy {
self.openai_account_affinity
}
+ #[must_use]
+ pub const fn with_switch_account_on_queue_failure(mut self, enabled: bool) -> Self {
+ self.switch_account_on_queue_failure = enabled;
+ self
+ }
+
+ /// 原队位由首次选号释放。仅对允许换号的请求调用,重选不排队,也不延长请求总期限
+ pub async fn reselect_after_queue_rejection<
+ T,
+ E: From,
+ F: Future
-
+
+
+ {{ t('settings.units.seconds') }}
+
+
+
+
+
+
+ {{ t('settings.units.seconds') }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('settings.concurrency.queueFailureDetails') }}
+
+
+
+
diff --git a/frontend/src/views/settings/components/TokenRefreshCard.vue b/frontend/src/views/settings/components/TokenRefreshCard.vue
index 53b1ced3b..ff226c486 100644
--- a/frontend/src/views/settings/components/TokenRefreshCard.vue
+++ b/frontend/src/views/settings/components/TokenRefreshCard.vue
@@ -14,10 +14,13 @@ const refreshConcurrency = defineModel('refreshConcurrency', { required:
-
+
+
+ {{ t('settings.units.seconds') }}
+
diff --git a/frontend/src/views/settings/components/error-protection/AccountErrorProtectionCard.vue b/frontend/src/views/settings/components/error-protection/AccountErrorProtectionCard.vue
index defc67f88..4a57cd92f 100644
--- a/frontend/src/views/settings/components/error-protection/AccountErrorProtectionCard.vue
+++ b/frontend/src/views/settings/components/error-protection/AccountErrorProtectionCard.vue
@@ -193,7 +193,7 @@ function removeRule(id: string) {
- {{ t('settings.errorProtection.seconds') }}
+ {{ t('settings.units.seconds') }}
@@ -216,7 +216,7 @@ function removeRule(id: string) {
- {{ t('settings.errorProtection.seconds') }}
+ {{ t('settings.units.seconds') }}
diff --git a/frontend/src/views/settings/composables/useSettingsForm.ts b/frontend/src/views/settings/composables/useSettingsForm.ts
index b4c0d3473..0b7f9a601 100644
--- a/frontend/src/views/settings/composables/useSettingsForm.ts
+++ b/frontend/src/views/settings/composables/useSettingsForm.ts
@@ -48,7 +48,9 @@ export function useSettingsForm() {
openaiSessionAffinityTtlHours: null as number | null,
maxWaitingPerKey: null as number | null,
maxWaitingPerAccount: null as number | null,
- concurrencyWaitTimeoutSeconds: null as number | null,
+ keyQueueTimeoutSeconds: null as number | null,
+ accountQueueTimeoutSeconds: null as number | null,
+ switchAccountOnQueueFailure: false,
maxRetryAfterSeconds: null as number | null,
responsesMaxDecompressedBodyMiB: null as number | null,
@@ -91,7 +93,7 @@ export function useSettingsForm() {
mappings.value = initial.mappings
}
- function numericModel(key: 'resetCreditMaxWeeklyRemainingPercent' | 'resetCreditLeadMinutes' | 'refreshMarginSeconds' | 'refreshConcurrency' | 'maxConcurrentPerAccount' | 'openaiGuardianReservedConcurrency' | 'maxAccountRotations' | 'openaiSessionAffinityTtlHours' | 'maxWaitingPerKey' | 'maxWaitingPerAccount' | 'concurrencyWaitTimeoutSeconds' | 'maxRetryAfterSeconds' | 'responsesMaxDecompressedBodyMiB' | 'usageRetentionDays') {
+ function numericModel(key: 'resetCreditMaxWeeklyRemainingPercent' | 'resetCreditLeadMinutes' | 'refreshMarginSeconds' | 'refreshConcurrency' | 'maxConcurrentPerAccount' | 'openaiGuardianReservedConcurrency' | 'maxAccountRotations' | 'openaiSessionAffinityTtlHours' | 'maxWaitingPerKey' | 'maxWaitingPerAccount' | 'keyQueueTimeoutSeconds' | 'accountQueueTimeoutSeconds' | 'maxRetryAfterSeconds' | 'responsesMaxDecompressedBodyMiB' | 'usageRetentionDays') {
return computed({
get: () => (form[key] === null
? ''
@@ -121,7 +123,8 @@ export function useSettingsForm() {
const resetCreditLeadMinutesValue = numericModel('resetCreditLeadMinutes')
const resetCreditMaxWeeklyRemainingPercentValue = numericModel('resetCreditMaxWeeklyRemainingPercent')
const usageRetentionDaysValue = numericModel('usageRetentionDays')
- const concurrencyWaitTimeoutSecondsValue = numericModel('concurrencyWaitTimeoutSeconds')
+ const keyQueueTimeoutSecondsValue = numericModel('keyQueueTimeoutSeconds')
+ const accountQueueTimeoutSecondsValue = numericModel('accountQueueTimeoutSeconds')
const maxRetryAfterSecondsValue = numericModel('maxRetryAfterSeconds')
function applySettings(data: Awaited>) {
@@ -138,7 +141,9 @@ export function useSettingsForm() {
form.openaiSessionAffinityTtlHours = data.openaiSessionAffinityTtlHours
form.maxWaitingPerKey = data.maxWaitingPerKey
form.maxWaitingPerAccount = data.maxWaitingPerAccount
- form.concurrencyWaitTimeoutSeconds = data.concurrencyWaitTimeoutSeconds
+ form.keyQueueTimeoutSeconds = data.keyQueueTimeoutSeconds
+ form.accountQueueTimeoutSeconds = data.accountQueueTimeoutSeconds
+ form.switchAccountOnQueueFailure = data.switchAccountOnQueueFailure
form.maxRetryAfterSeconds = data.maxRetryAfterSeconds
form.responsesMaxDecompressedBodyMiB = data.responsesMaxDecompressedBodyBytes / MIB
@@ -238,7 +243,7 @@ export function useSettingsForm() {
const savedSettings = saved.value
if (saving.value || loading.value || detectingLocation.value || !savedSettings)
return
- const { refreshMarginSeconds, refreshConcurrency, maxConcurrentPerAccount, openaiGuardianReservedConcurrency, openaiAccountAffinity, maxAccountRotations, openaiSessionAffinityTtlHours, rotationStrategy, maxWaitingPerKey, maxWaitingPerAccount, concurrencyWaitTimeoutSeconds, maxRetryAfterSeconds, responsesMaxDecompressedBodyMiB } = form
+ const { refreshMarginSeconds, refreshConcurrency, maxConcurrentPerAccount, openaiGuardianReservedConcurrency, openaiAccountAffinity, maxAccountRotations, openaiSessionAffinityTtlHours, rotationStrategy, maxWaitingPerKey, maxWaitingPerAccount, keyQueueTimeoutSeconds, accountQueueTimeoutSeconds, maxRetryAfterSeconds, responsesMaxDecompressedBodyMiB } = form
const { resetCreditMaxWeeklyRemainingPercent, resetCreditLeadMinutes, usageRetentionDays } = form
// 关闭时保留已保存的自定义值,未完成的草稿不阻止停止覆盖。
const requestLocation = form.requestLocationEnabled
@@ -267,7 +272,9 @@ export function useSettingsForm() {
openaiSessionAffinityTtlHours: openaiSessionAffinityTtlHours!,
maxWaitingPerKey: maxWaitingPerKey!,
maxWaitingPerAccount: maxWaitingPerAccount!,
- concurrencyWaitTimeoutSeconds: concurrencyWaitTimeoutSeconds!,
+ keyQueueTimeoutSeconds: keyQueueTimeoutSeconds!,
+ accountQueueTimeoutSeconds: accountQueueTimeoutSeconds!,
+ switchAccountOnQueueFailure: form.switchAccountOnQueueFailure,
maxRetryAfterSeconds: maxRetryAfterSeconds!,
responsesMaxDecompressedBodyBytes: responsesMaxDecompressedBodyMiB! * MIB,
rotationStrategy: rotationStrategy as RotationStrategy,
@@ -327,7 +334,8 @@ export function useSettingsForm() {
openaiSessionAffinityTtlHoursValue,
maxWaitingPerKeyValue,
maxWaitingPerAccountValue,
- concurrencyWaitTimeoutSecondsValue,
+ keyQueueTimeoutSecondsValue,
+ accountQueueTimeoutSecondsValue,
maxRetryAfterSecondsValue,
responsesMaxDecompressedBodyMiBValue,
usageRetentionDaysValue,
diff --git a/frontend/src/views/settings/composables/useSettingsFormRules.ts b/frontend/src/views/settings/composables/useSettingsFormRules.ts
index fcd83c844..39e150ed7 100644
--- a/frontend/src/views/settings/composables/useSettingsFormRules.ts
+++ b/frontend/src/views/settings/composables/useSettingsFormRules.ts
@@ -38,7 +38,8 @@ export function useSettingsFormRules(form: () => SettingsForm) {
'openaiSessionAffinityTtlHours': integer(1, 720, t('settings.form.invalidAffinityDuration')),
'maxWaitingPerKey': integer(0, 1000, t('settings.form.invalidQueueSettings')),
'maxWaitingPerAccount': integer(0, 1000, t('settings.form.invalidQueueSettings')),
- 'concurrencyWaitTimeoutSeconds': integer(1, 120, t('settings.form.invalidQueueSettings')),
+ 'keyQueueTimeoutSeconds': integer(1, 120, t('settings.form.invalidQueueSettings')),
+ 'accountQueueTimeoutSeconds': integer(1, 120, t('settings.form.invalidQueueSettings')),
'maxRetryAfterSeconds': integer(0, 4_294_967_295, t('settings.form.invalidRetryAfterLimit')),
'responsesMaxDecompressedBodyMiB': integer(1, Math.floor(Number.MAX_SAFE_INTEGER / 1024 / 1024), t('settings.form.invalidBodyLimit')),
'resetCreditLeadMinutes': integer(1, 1440, t('settings.form.invalidResetLeadTime')),
diff --git a/frontend/src/views/settings/index.vue b/frontend/src/views/settings/index.vue
index 045dd7769..10f5d3ca8 100644
--- a/frontend/src/views/settings/index.vue
+++ b/frontend/src/views/settings/index.vue
@@ -94,7 +94,8 @@ const {
openaiSessionAffinityTtlHoursValue,
maxWaitingPerKeyValue,
maxWaitingPerAccountValue,
- concurrencyWaitTimeoutSecondsValue,
+ keyQueueTimeoutSecondsValue,
+ accountQueueTimeoutSecondsValue,
maxRetryAfterSecondsValue,
responsesMaxDecompressedBodyMiBValue,
usageRetentionDaysValue,
@@ -210,7 +211,9 @@ watch(section, (value) => {
v-model:max-concurrent-per-account="maxConcurrentPerAccountValue"
v-model:max-waiting-per-key="maxWaitingPerKeyValue"
v-model:max-waiting-per-account="maxWaitingPerAccountValue"
- v-model:concurrency-wait-timeout-seconds="concurrencyWaitTimeoutSecondsValue"
+ v-model:key-queue-timeout-seconds="keyQueueTimeoutSecondsValue"
+ v-model:account-queue-timeout-seconds="accountQueueTimeoutSecondsValue"
+ v-model:switch-account-on-queue-failure="form.switchAccountOnQueueFailure"
v-model:max-retry-after-seconds="maxRetryAfterSecondsValue"
v-model:openai-guardian-reserved-concurrency="openaiGuardianReservedConcurrencyValue"
/>