"frontend/src/i18n/vscode:/vscode.git/clone" did not exist on "4e75d8fda9f010e856741328d9a49ee66a1b3a53"
Unverified Commit 63a8c769 authored by Wesley Liddick's avatar Wesley Liddick Committed by GitHub
Browse files

Merge pull request #798 from touwaeriol/feature/account-load-factor

feat: add account load_factor for scheduling load calculation
parents f355a68b c87e6526
...@@ -1252,7 +1252,7 @@ func (s *OpenAIGatewayService) SelectAccountWithLoadAwareness(ctx context.Contex ...@@ -1252,7 +1252,7 @@ func (s *OpenAIGatewayService) SelectAccountWithLoadAwareness(ctx context.Contex
for _, acc := range candidates { for _, acc := range candidates {
accountLoads = append(accountLoads, AccountWithConcurrency{ accountLoads = append(accountLoads, AccountWithConcurrency{
ID: acc.ID, ID: acc.ID,
MaxConcurrency: acc.Concurrency, MaxConcurrency: acc.EffectiveLoadFactor(),
}) })
} }
......
...@@ -64,8 +64,12 @@ func (s *OpsService) getAccountsLoadMapBestEffort(ctx context.Context, accounts ...@@ -64,8 +64,12 @@ func (s *OpsService) getAccountsLoadMapBestEffort(ctx context.Context, accounts
if acc.ID <= 0 { if acc.ID <= 0 {
continue continue
} }
if prev, ok := unique[acc.ID]; !ok || acc.Concurrency > prev { c := acc.Concurrency
unique[acc.ID] = acc.Concurrency if c <= 0 {
c = 1
}
if prev, ok := unique[acc.ID]; !ok || c > prev {
unique[acc.ID] = c
} }
} }
......
...@@ -389,13 +389,9 @@ func (c *OpsMetricsCollector) collectConcurrencyQueueDepth(parentCtx context.Con ...@@ -389,13 +389,9 @@ func (c *OpsMetricsCollector) collectConcurrencyQueueDepth(parentCtx context.Con
if acc.ID <= 0 { if acc.ID <= 0 {
continue continue
} }
maxConc := acc.Concurrency
if maxConc < 0 {
maxConc = 0
}
batch = append(batch, AccountWithConcurrency{ batch = append(batch, AccountWithConcurrency{
ID: acc.ID, ID: acc.ID,
MaxConcurrency: maxConc, MaxConcurrency: acc.Concurrency,
}) })
} }
if len(batch) == 0 { if len(batch) == 0 {
......
ALTER TABLE accounts ADD COLUMN IF NOT EXISTS load_factor INTEGER;
...@@ -469,7 +469,7 @@ ...@@ -469,7 +469,7 @@
</div> </div>
<!-- Concurrency & Priority --> <!-- Concurrency & Priority -->
<div class="grid grid-cols-2 gap-4 border-t border-gray-200 pt-4 dark:border-dark-600 lg:grid-cols-3"> <div class="grid grid-cols-2 gap-4 border-t border-gray-200 pt-4 dark:border-dark-600 lg:grid-cols-4">
<div> <div>
<div class="mb-3 flex items-center justify-between"> <div class="mb-3 flex items-center justify-between">
<label <label
...@@ -496,8 +496,39 @@ ...@@ -496,8 +496,39 @@
class="input" class="input"
:class="!enableConcurrency && 'cursor-not-allowed opacity-50'" :class="!enableConcurrency && 'cursor-not-allowed opacity-50'"
aria-labelledby="bulk-edit-concurrency-label" aria-labelledby="bulk-edit-concurrency-label"
@input="concurrency = Math.max(1, concurrency || 1)"
/> />
</div> </div>
<div>
<div class="mb-3 flex items-center justify-between">
<label
id="bulk-edit-load-factor-label"
class="input-label mb-0"
for="bulk-edit-load-factor-enabled"
>
{{ t('admin.accounts.loadFactor') }}
</label>
<input
v-model="enableLoadFactor"
id="bulk-edit-load-factor-enabled"
type="checkbox"
aria-controls="bulk-edit-load-factor"
class="rounded border-gray-300 text-primary-600 focus:ring-primary-500"
/>
</div>
<input
v-model.number="loadFactor"
id="bulk-edit-load-factor"
type="number"
min="1"
:disabled="!enableLoadFactor"
class="input"
:class="!enableLoadFactor && 'cursor-not-allowed opacity-50'"
aria-labelledby="bulk-edit-load-factor-label"
@input="loadFactor = (loadFactor &amp;&amp; loadFactor >= 1) ? loadFactor : null"
/>
<p class="input-hint">{{ t('admin.accounts.loadFactorHint') }}</p>
</div>
<div> <div>
<div class="mb-3 flex items-center justify-between"> <div class="mb-3 flex items-center justify-between">
<label <label
...@@ -869,6 +900,7 @@ const enableCustomErrorCodes = ref(false) ...@@ -869,6 +900,7 @@ const enableCustomErrorCodes = ref(false)
const enableInterceptWarmup = ref(false) const enableInterceptWarmup = ref(false)
const enableProxy = ref(false) const enableProxy = ref(false)
const enableConcurrency = ref(false) const enableConcurrency = ref(false)
const enableLoadFactor = ref(false)
const enablePriority = ref(false) const enablePriority = ref(false)
const enableRateMultiplier = ref(false) const enableRateMultiplier = ref(false)
const enableStatus = ref(false) const enableStatus = ref(false)
...@@ -889,6 +921,7 @@ const customErrorCodeInput = ref<number | null>(null) ...@@ -889,6 +921,7 @@ const customErrorCodeInput = ref<number | null>(null)
const interceptWarmupRequests = ref(false) const interceptWarmupRequests = ref(false)
const proxyId = ref<number | null>(null) const proxyId = ref<number | null>(null)
const concurrency = ref(1) const concurrency = ref(1)
const loadFactor = ref<number | null>(null)
const priority = ref(1) const priority = ref(1)
const rateMultiplier = ref(1) const rateMultiplier = ref(1)
const status = ref<'active' | 'inactive'>('active') const status = ref<'active' | 'inactive'>('active')
...@@ -1195,6 +1228,12 @@ const buildUpdatePayload = (): Record<string, unknown> | null => { ...@@ -1195,6 +1228,12 @@ const buildUpdatePayload = (): Record<string, unknown> | null => {
updates.concurrency = concurrency.value updates.concurrency = concurrency.value
} }
if (enableLoadFactor.value) {
// 空值/NaN/0 时发送 0(后端约定 <= 0 表示清除)
const lf = loadFactor.value
updates.load_factor = (lf != null && !Number.isNaN(lf) && lf > 0) ? lf : 0
}
if (enablePriority.value) { if (enablePriority.value) {
updates.priority = priority.value updates.priority = priority.value
} }
...@@ -1340,6 +1379,7 @@ const handleSubmit = async () => { ...@@ -1340,6 +1379,7 @@ const handleSubmit = async () => {
enableInterceptWarmup.value || enableInterceptWarmup.value ||
enableProxy.value || enableProxy.value ||
enableConcurrency.value || enableConcurrency.value ||
enableLoadFactor.value ||
enablePriority.value || enablePriority.value ||
enableRateMultiplier.value || enableRateMultiplier.value ||
enableStatus.value || enableStatus.value ||
...@@ -1430,6 +1470,7 @@ watch( ...@@ -1430,6 +1470,7 @@ watch(
enableInterceptWarmup.value = false enableInterceptWarmup.value = false
enableProxy.value = false enableProxy.value = false
enableConcurrency.value = false enableConcurrency.value = false
enableLoadFactor.value = false
enablePriority.value = false enablePriority.value = false
enableRateMultiplier.value = false enableRateMultiplier.value = false
enableStatus.value = false enableStatus.value = false
...@@ -1446,6 +1487,7 @@ watch( ...@@ -1446,6 +1487,7 @@ watch(
interceptWarmupRequests.value = false interceptWarmupRequests.value = false
proxyId.value = null proxyId.value = null
concurrency.value = 1 concurrency.value = 1
loadFactor.value = null
priority.value = 1 priority.value = 1
rateMultiplier.value = 1 rateMultiplier.value = 1
status.value = 'active' status.value = 'active'
......
...@@ -1752,10 +1752,18 @@ ...@@ -1752,10 +1752,18 @@
<ProxySelector v-model="form.proxy_id" :proxies="proxies" /> <ProxySelector v-model="form.proxy_id" :proxies="proxies" />
</div> </div>
<div class="grid grid-cols-2 gap-4 lg:grid-cols-3"> <div class="grid grid-cols-2 gap-4 lg:grid-cols-4">
<div> <div>
<label class="input-label">{{ t('admin.accounts.concurrency') }}</label> <label class="input-label">{{ t('admin.accounts.concurrency') }}</label>
<input v-model.number="form.concurrency" type="number" min="1" class="input" /> <input v-model.number="form.concurrency" type="number" min="1" class="input"
@input="form.concurrency = Math.max(1, form.concurrency || 1)" />
</div>
<div>
<label class="input-label">{{ t('admin.accounts.loadFactor') }}</label>
<input v-model.number="form.load_factor" type="number" min="1"
class="input" :placeholder="String(form.concurrency || 1)"
@input="form.load_factor = (form.load_factor &amp;&amp; form.load_factor >= 1) ? form.load_factor : null" />
<p class="input-hint">{{ t('admin.accounts.loadFactorHint') }}</p>
</div> </div>
<div> <div>
<label class="input-label">{{ t('admin.accounts.priority') }}</label> <label class="input-label">{{ t('admin.accounts.priority') }}</label>
...@@ -2638,6 +2646,7 @@ const form = reactive({ ...@@ -2638,6 +2646,7 @@ const form = reactive({
credentials: {} as Record<string, unknown>, credentials: {} as Record<string, unknown>,
proxy_id: null as number | null, proxy_id: null as number | null,
concurrency: 10, concurrency: 10,
load_factor: null as number | null,
priority: 1, priority: 1,
rate_multiplier: 1, rate_multiplier: 1,
group_ids: [] as number[], group_ids: [] as number[],
...@@ -3117,6 +3126,7 @@ const resetForm = () => { ...@@ -3117,6 +3126,7 @@ const resetForm = () => {
form.credentials = {} form.credentials = {}
form.proxy_id = null form.proxy_id = null
form.concurrency = 10 form.concurrency = 10
form.load_factor = null
form.priority = 1 form.priority = 1
form.rate_multiplier = 1 form.rate_multiplier = 1
form.group_ids = [] form.group_ids = []
...@@ -3489,6 +3499,7 @@ const handleImportAccessToken = async (accessTokenInput: string) => { ...@@ -3489,6 +3499,7 @@ const handleImportAccessToken = async (accessTokenInput: string) => {
extra: soraExtra, extra: soraExtra,
proxy_id: form.proxy_id, proxy_id: form.proxy_id,
concurrency: form.concurrency, concurrency: form.concurrency,
load_factor: form.load_factor ?? undefined,
priority: form.priority, priority: form.priority,
rate_multiplier: form.rate_multiplier, rate_multiplier: form.rate_multiplier,
group_ids: form.group_ids, group_ids: form.group_ids,
...@@ -3553,6 +3564,7 @@ const createAccountAndFinish = async ( ...@@ -3553,6 +3564,7 @@ const createAccountAndFinish = async (
extra: finalExtra, extra: finalExtra,
proxy_id: form.proxy_id, proxy_id: form.proxy_id,
concurrency: form.concurrency, concurrency: form.concurrency,
load_factor: form.load_factor ?? undefined,
priority: form.priority, priority: form.priority,
rate_multiplier: form.rate_multiplier, rate_multiplier: form.rate_multiplier,
group_ids: form.group_ids, group_ids: form.group_ids,
...@@ -3608,6 +3620,7 @@ const handleOpenAIExchange = async (authCode: string) => { ...@@ -3608,6 +3620,7 @@ const handleOpenAIExchange = async (authCode: string) => {
extra, extra,
proxy_id: form.proxy_id, proxy_id: form.proxy_id,
concurrency: form.concurrency, concurrency: form.concurrency,
load_factor: form.load_factor ?? undefined,
priority: form.priority, priority: form.priority,
rate_multiplier: form.rate_multiplier, rate_multiplier: form.rate_multiplier,
group_ids: form.group_ids, group_ids: form.group_ids,
...@@ -3637,6 +3650,7 @@ const handleOpenAIExchange = async (authCode: string) => { ...@@ -3637,6 +3650,7 @@ const handleOpenAIExchange = async (authCode: string) => {
extra: soraExtra, extra: soraExtra,
proxy_id: form.proxy_id, proxy_id: form.proxy_id,
concurrency: form.concurrency, concurrency: form.concurrency,
load_factor: form.load_factor ?? undefined,
priority: form.priority, priority: form.priority,
rate_multiplier: form.rate_multiplier, rate_multiplier: form.rate_multiplier,
group_ids: form.group_ids, group_ids: form.group_ids,
...@@ -3714,6 +3728,7 @@ const handleOpenAIValidateRT = async (refreshTokenInput: string) => { ...@@ -3714,6 +3728,7 @@ const handleOpenAIValidateRT = async (refreshTokenInput: string) => {
extra, extra,
proxy_id: form.proxy_id, proxy_id: form.proxy_id,
concurrency: form.concurrency, concurrency: form.concurrency,
load_factor: form.load_factor ?? undefined,
priority: form.priority, priority: form.priority,
rate_multiplier: form.rate_multiplier, rate_multiplier: form.rate_multiplier,
group_ids: form.group_ids, group_ids: form.group_ids,
...@@ -3741,6 +3756,7 @@ const handleOpenAIValidateRT = async (refreshTokenInput: string) => { ...@@ -3741,6 +3756,7 @@ const handleOpenAIValidateRT = async (refreshTokenInput: string) => {
extra: soraExtra, extra: soraExtra,
proxy_id: form.proxy_id, proxy_id: form.proxy_id,
concurrency: form.concurrency, concurrency: form.concurrency,
load_factor: form.load_factor ?? undefined,
priority: form.priority, priority: form.priority,
rate_multiplier: form.rate_multiplier, rate_multiplier: form.rate_multiplier,
group_ids: form.group_ids, group_ids: form.group_ids,
...@@ -3829,6 +3845,7 @@ const handleSoraValidateST = async (sessionTokenInput: string) => { ...@@ -3829,6 +3845,7 @@ const handleSoraValidateST = async (sessionTokenInput: string) => {
extra: soraExtra, extra: soraExtra,
proxy_id: form.proxy_id, proxy_id: form.proxy_id,
concurrency: form.concurrency, concurrency: form.concurrency,
load_factor: form.load_factor ?? undefined,
priority: form.priority, priority: form.priority,
rate_multiplier: form.rate_multiplier, rate_multiplier: form.rate_multiplier,
group_ids: form.group_ids, group_ids: form.group_ids,
...@@ -3917,6 +3934,7 @@ const handleAntigravityValidateRT = async (refreshTokenInput: string) => { ...@@ -3917,6 +3934,7 @@ const handleAntigravityValidateRT = async (refreshTokenInput: string) => {
extra: {}, extra: {},
proxy_id: form.proxy_id, proxy_id: form.proxy_id,
concurrency: form.concurrency, concurrency: form.concurrency,
load_factor: form.load_factor ?? undefined,
priority: form.priority, priority: form.priority,
rate_multiplier: form.rate_multiplier, rate_multiplier: form.rate_multiplier,
group_ids: form.group_ids, group_ids: form.group_ids,
...@@ -4075,8 +4093,11 @@ const handleAnthropicExchange = async (authCode: string) => { ...@@ -4075,8 +4093,11 @@ const handleAnthropicExchange = async (authCode: string) => {
} }
// Add RPM limit settings // Add RPM limit settings
if (rpmLimitEnabled.value && baseRpm.value != null && baseRpm.value > 0) { if (rpmLimitEnabled.value) {
extra.base_rpm = baseRpm.value const DEFAULT_BASE_RPM = 15
extra.base_rpm = (baseRpm.value != null && baseRpm.value > 0)
? baseRpm.value
: DEFAULT_BASE_RPM
extra.rpm_strategy = rpmStrategy.value extra.rpm_strategy = rpmStrategy.value
if (rpmStickyBuffer.value != null && rpmStickyBuffer.value > 0) { if (rpmStickyBuffer.value != null && rpmStickyBuffer.value > 0) {
extra.rpm_sticky_buffer = rpmStickyBuffer.value extra.rpm_sticky_buffer = rpmStickyBuffer.value
...@@ -4187,8 +4208,11 @@ const handleCookieAuth = async (sessionKey: string) => { ...@@ -4187,8 +4208,11 @@ const handleCookieAuth = async (sessionKey: string) => {
} }
// Add RPM limit settings // Add RPM limit settings
if (rpmLimitEnabled.value && baseRpm.value != null && baseRpm.value > 0) { if (rpmLimitEnabled.value) {
extra.base_rpm = baseRpm.value const DEFAULT_BASE_RPM = 15
extra.base_rpm = (baseRpm.value != null && baseRpm.value > 0)
? baseRpm.value
: DEFAULT_BASE_RPM
extra.rpm_strategy = rpmStrategy.value extra.rpm_strategy = rpmStrategy.value
if (rpmStickyBuffer.value != null && rpmStickyBuffer.value > 0) { if (rpmStickyBuffer.value != null && rpmStickyBuffer.value > 0) {
extra.rpm_sticky_buffer = rpmStickyBuffer.value extra.rpm_sticky_buffer = rpmStickyBuffer.value
...@@ -4234,6 +4258,7 @@ const handleCookieAuth = async (sessionKey: string) => { ...@@ -4234,6 +4258,7 @@ const handleCookieAuth = async (sessionKey: string) => {
extra, extra,
proxy_id: form.proxy_id, proxy_id: form.proxy_id,
concurrency: form.concurrency, concurrency: form.concurrency,
load_factor: form.load_factor ?? undefined,
priority: form.priority, priority: form.priority,
rate_multiplier: form.rate_multiplier, rate_multiplier: form.rate_multiplier,
group_ids: form.group_ids, group_ids: form.group_ids,
......
...@@ -650,10 +650,18 @@ ...@@ -650,10 +650,18 @@
<ProxySelector v-model="form.proxy_id" :proxies="proxies" /> <ProxySelector v-model="form.proxy_id" :proxies="proxies" />
</div> </div>
<div class="grid grid-cols-2 gap-4 lg:grid-cols-3"> <div class="grid grid-cols-2 gap-4 lg:grid-cols-4">
<div> <div>
<label class="input-label">{{ t('admin.accounts.concurrency') }}</label> <label class="input-label">{{ t('admin.accounts.concurrency') }}</label>
<input v-model.number="form.concurrency" type="number" min="1" class="input" /> <input v-model.number="form.concurrency" type="number" min="1" class="input"
@input="form.concurrency = Math.max(1, form.concurrency || 1)" />
</div>
<div>
<label class="input-label">{{ t('admin.accounts.loadFactor') }}</label>
<input v-model.number="form.load_factor" type="number" min="1"
class="input" :placeholder="String(form.concurrency || 1)"
@input="form.load_factor = (form.load_factor &amp;&amp; form.load_factor >= 1) ? form.load_factor : null" />
<p class="input-hint">{{ t('admin.accounts.loadFactorHint') }}</p>
</div> </div>
<div> <div>
<label class="input-label">{{ t('admin.accounts.priority') }}</label> <label class="input-label">{{ t('admin.accounts.priority') }}</label>
...@@ -1470,6 +1478,7 @@ const form = reactive({ ...@@ -1470,6 +1478,7 @@ const form = reactive({
notes: '', notes: '',
proxy_id: null as number | null, proxy_id: null as number | null,
concurrency: 1, concurrency: 1,
load_factor: null as number | null,
priority: 1, priority: 1,
rate_multiplier: 1, rate_multiplier: 1,
status: 'active' as 'active' | 'inactive', status: 'active' as 'active' | 'inactive',
...@@ -1503,9 +1512,12 @@ watch( ...@@ -1503,9 +1512,12 @@ watch(
form.notes = newAccount.notes || '' form.notes = newAccount.notes || ''
form.proxy_id = newAccount.proxy_id form.proxy_id = newAccount.proxy_id
form.concurrency = newAccount.concurrency form.concurrency = newAccount.concurrency
form.load_factor = newAccount.load_factor ?? null
form.priority = newAccount.priority form.priority = newAccount.priority
form.rate_multiplier = newAccount.rate_multiplier ?? 1 form.rate_multiplier = newAccount.rate_multiplier ?? 1
form.status = newAccount.status as 'active' | 'inactive' form.status = (newAccount.status === 'active' || newAccount.status === 'inactive')
? newAccount.status
: 'active'
form.group_ids = newAccount.group_ids || [] form.group_ids = newAccount.group_ids || []
form.expires_at = newAccount.expires_at ?? null form.expires_at = newAccount.expires_at ?? null
...@@ -2053,6 +2065,11 @@ const handleSubmit = async () => { ...@@ -2053,6 +2065,11 @@ const handleSubmit = async () => {
if (!props.account) return if (!props.account) return
const accountID = props.account.id const accountID = props.account.id
if (form.status !== 'active' && form.status !== 'inactive') {
appStore.showError(t('admin.accounts.pleaseSelectStatus'))
return
}
const updatePayload: Record<string, unknown> = { ...form } const updatePayload: Record<string, unknown> = { ...form }
try { try {
// 后端期望 proxy_id: 0 表示清除代理,而不是 null // 后端期望 proxy_id: 0 表示清除代理,而不是 null
...@@ -2062,6 +2079,11 @@ const handleSubmit = async () => { ...@@ -2062,6 +2079,11 @@ const handleSubmit = async () => {
if (form.expires_at === null) { if (form.expires_at === null) {
updatePayload.expires_at = 0 updatePayload.expires_at = 0
} }
// load_factor: 空值/NaN/0/负数 时发送 0(后端约定 <= 0 = 清除)
const lf = form.load_factor
if (lf == null || Number.isNaN(lf) || lf <= 0) {
updatePayload.load_factor = 0
}
updatePayload.auto_pause_on_expired = autoPauseOnExpired.value updatePayload.auto_pause_on_expired = autoPauseOnExpired.value
// For apikey type, handle credentials update // For apikey type, handle credentials update
...@@ -2201,8 +2223,11 @@ const handleSubmit = async () => { ...@@ -2201,8 +2223,11 @@ const handleSubmit = async () => {
} }
// RPM limit settings // RPM limit settings
if (rpmLimitEnabled.value && baseRpm.value != null && baseRpm.value > 0) { if (rpmLimitEnabled.value) {
newExtra.base_rpm = baseRpm.value const DEFAULT_BASE_RPM = 15
newExtra.base_rpm = (baseRpm.value != null && baseRpm.value > 0)
? baseRpm.value
: DEFAULT_BASE_RPM
newExtra.rpm_strategy = rpmStrategy.value newExtra.rpm_strategy = rpmStrategy.value
if (rpmStickyBuffer.value != null && rpmStickyBuffer.value > 0) { if (rpmStickyBuffer.value != null && rpmStickyBuffer.value > 0) {
newExtra.rpm_sticky_buffer = rpmStickyBuffer.value newExtra.rpm_sticky_buffer = rpmStickyBuffer.value
......
...@@ -2003,10 +2003,12 @@ export default { ...@@ -2003,10 +2003,12 @@ export default {
proxy: 'Proxy', proxy: 'Proxy',
noProxy: 'No Proxy', noProxy: 'No Proxy',
concurrency: 'Concurrency', concurrency: 'Concurrency',
loadFactor: 'Load Factor',
loadFactorHint: 'Higher load factor increases scheduling frequency',
priority: 'Priority', priority: 'Priority',
priorityHint: 'Lower value accounts are used first', priorityHint: 'Lower value accounts are used first',
billingRateMultiplier: 'Billing Rate Multiplier', billingRateMultiplier: 'Billing Rate Multiplier',
billingRateMultiplierHint: '>=0, 0 means free. Affects account billing only', billingRateMultiplierHint: '0 = free, affects account billing only',
expiresAt: 'Expires At', expiresAt: 'Expires At',
expiresAtHint: 'Leave empty for no expiration', expiresAtHint: 'Leave empty for no expiration',
higherPriorityFirst: 'Lower value means higher priority', higherPriorityFirst: 'Lower value means higher priority',
...@@ -2022,6 +2024,7 @@ export default { ...@@ -2022,6 +2024,7 @@ export default {
accountUpdated: 'Account updated successfully', accountUpdated: 'Account updated successfully',
failedToCreate: 'Failed to create account', failedToCreate: 'Failed to create account',
failedToUpdate: 'Failed to update account', failedToUpdate: 'Failed to update account',
pleaseSelectStatus: 'Please select a valid account status',
mixedChannelWarningTitle: 'Mixed Channel Warning', mixedChannelWarningTitle: 'Mixed Channel Warning',
mixedChannelWarning: 'Warning: Group "{groupName}" contains both {currentPlatform} and {otherPlatform} accounts. Mixing different channels may cause thinking block signature validation issues, which will fallback to non-thinking mode. Are you sure you want to continue?', mixedChannelWarning: 'Warning: Group "{groupName}" contains both {currentPlatform} and {otherPlatform} accounts. Mixing different channels may cause thinking block signature validation issues, which will fallback to non-thinking mode. Are you sure you want to continue?',
pleaseEnterAccountName: 'Please enter account name', pleaseEnterAccountName: 'Please enter account name',
......
...@@ -2145,10 +2145,12 @@ export default { ...@@ -2145,10 +2145,12 @@ export default {
proxy: '代理', proxy: '代理',
noProxy: '无代理', noProxy: '无代理',
concurrency: '并发数', concurrency: '并发数',
loadFactor: '负载因子',
loadFactorHint: '提高负载因子可以提高对账号的调度频率',
priority: '优先级', priority: '优先级',
priorityHint: '优先级越小的账号优先使用', priorityHint: '优先级越小的账号优先使用',
billingRateMultiplier: '账号计费倍率', billingRateMultiplier: '账号计费倍率',
billingRateMultiplierHint: '>=0,0 表示该账号计费为 0;仅影响账号计费口径', billingRateMultiplierHint: '0 表示不计费,仅影响账号计费',
expiresAt: '过期时间', expiresAt: '过期时间',
expiresAtHint: '留空表示不过期', expiresAtHint: '留空表示不过期',
higherPriorityFirst: '数值越小优先级越高', higherPriorityFirst: '数值越小优先级越高',
...@@ -2164,6 +2166,7 @@ export default { ...@@ -2164,6 +2166,7 @@ export default {
accountUpdated: '账号更新成功', accountUpdated: '账号更新成功',
failedToCreate: '创建账号失败', failedToCreate: '创建账号失败',
failedToUpdate: '更新账号失败', failedToUpdate: '更新账号失败',
pleaseSelectStatus: '请选择有效的账号状态',
mixedChannelWarningTitle: '混合渠道警告', mixedChannelWarningTitle: '混合渠道警告',
mixedChannelWarning: '警告:分组 "{groupName}" 中同时包含 {currentPlatform} 和 {otherPlatform} 账号。混合使用不同渠道可能导致 thinking block 签名验证问题,会自动回退到非 thinking 模式。确定要继续吗?', mixedChannelWarning: '警告:分组 "{groupName}" 中同时包含 {currentPlatform} 和 {otherPlatform} 账号。混合使用不同渠道可能导致 thinking block 签名验证问题,会自动回退到非 thinking 模式。确定要继续吗?',
pleaseEnterAccountName: '请输入账号名称', pleaseEnterAccountName: '请输入账号名称',
......
...@@ -653,6 +653,7 @@ export interface Account { ...@@ -653,6 +653,7 @@ export interface Account {
} & Record<string, unknown>) } & Record<string, unknown>)
proxy_id: number | null proxy_id: number | null
concurrency: number concurrency: number
load_factor?: number | null
current_concurrency?: number // Real-time concurrency count from Redis current_concurrency?: number // Real-time concurrency count from Redis
priority: number priority: number
rate_multiplier?: number // Account billing multiplier (>=0, 0 means free) rate_multiplier?: number // Account billing multiplier (>=0, 0 means free)
...@@ -787,6 +788,7 @@ export interface CreateAccountRequest { ...@@ -787,6 +788,7 @@ export interface CreateAccountRequest {
extra?: Record<string, unknown> extra?: Record<string, unknown>
proxy_id?: number | null proxy_id?: number | null
concurrency?: number concurrency?: number
load_factor?: number | null
priority?: number priority?: number
rate_multiplier?: number // Account billing multiplier (>=0, 0 means free) rate_multiplier?: number // Account billing multiplier (>=0, 0 means free)
group_ids?: number[] group_ids?: number[]
...@@ -803,6 +805,7 @@ export interface UpdateAccountRequest { ...@@ -803,6 +805,7 @@ export interface UpdateAccountRequest {
extra?: Record<string, unknown> extra?: Record<string, unknown>
proxy_id?: number | null proxy_id?: number | null
concurrency?: number concurrency?: number
load_factor?: number | null
priority?: number priority?: number
rate_multiplier?: number // Account billing multiplier (>=0, 0 means free) rate_multiplier?: number // Account billing multiplier (>=0, 0 means free)
schedulable?: boolean schedulable?: boolean
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment