Merge branch 'main' into release/custom-0.1.91

# Conflicts:
#	frontend/src/components/admin/account/AccountActionMenu.vue
#	frontend/src/views/admin/AccountsView.vue
This commit is contained in:
erio
2026-03-06 04:08:14 +08:00
97 changed files with 6442 additions and 311 deletions

View File

@@ -1227,6 +1227,9 @@
</div>
<!-- API Key 账号配额限制 -->
<QuotaLimitCard v-if="form.type === 'apikey'" v-model="editQuotaLimit" />
<!-- Temp Unschedulable Rules -->
<div class="border-t border-gray-200 pt-4 dark:border-dark-600 space-y-4">
<div class="mb-3 flex items-center justify-between">
@@ -1749,11 +1752,17 @@
<ProxySelector v-model="form.proxy_id" :proxies="proxies" />
</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>
<label class="input-label">{{ t('admin.accounts.concurrency') }}</label>
<input v-model.number="form.concurrency" type="number" min="1" class="input" />
</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)" />
<p class="input-hint">{{ t('admin.accounts.loadFactorHint') }}</p>
</div>
<div>
<label class="input-label">{{ t('admin.accounts.priority') }}</label>
<input
@@ -2337,6 +2346,7 @@ import Icon from '@/components/icons/Icon.vue'
import ProxySelector from '@/components/common/ProxySelector.vue'
import GroupSelector from '@/components/common/GroupSelector.vue'
import ModelWhitelistSelector from '@/components/account/ModelWhitelistSelector.vue'
import QuotaLimitCard from '@/components/account/QuotaLimitCard.vue'
import { applyInterceptWarmup } from '@/components/account/credentialsBuilder'
import { formatDateTimeLocalInput, parseDateTimeLocalInput } from '@/utils/format'
import { createStableObjectKeyResolver } from '@/utils/stableObjectKey'
@@ -2460,6 +2470,7 @@ const accountCategory = ref<'oauth-based' | 'apikey'>('oauth-based') // UI selec
const addMethod = ref<AddMethod>('oauth') // For oauth-based: 'oauth' or 'setup-token'
const apiKeyBaseUrl = ref('https://api.anthropic.com')
const apiKeyValue = ref('')
const editQuotaLimit = ref<number | null>(null)
const modelMappings = ref<ModelMapping[]>([])
const modelRestrictionMode = ref<'whitelist' | 'mapping'>('whitelist')
const allowedModels = ref<string[]>([])
@@ -2633,6 +2644,7 @@ const form = reactive({
credentials: {} as Record<string, unknown>,
proxy_id: null as number | null,
concurrency: 10,
load_factor: null as number | null,
priority: 1,
rate_multiplier: 1,
group_ids: [] as number[],
@@ -3112,6 +3124,7 @@ const resetForm = () => {
form.credentials = {}
form.proxy_id = null
form.concurrency = 10
form.load_factor = null
form.priority = 1
form.rate_multiplier = 1
form.group_ids = []
@@ -3120,6 +3133,7 @@ const resetForm = () => {
addMethod.value = 'oauth'
apiKeyBaseUrl.value = 'https://api.anthropic.com'
apiKeyValue.value = ''
editQuotaLimit.value = null
modelMappings.value = []
modelRestrictionMode.value = 'whitelist'
allowedModels.value = [...claudeModels] // Default fill related models
@@ -3483,6 +3497,7 @@ const handleImportAccessToken = async (accessTokenInput: string) => {
extra: soraExtra,
proxy_id: form.proxy_id,
concurrency: form.concurrency,
load_factor: form.load_factor || undefined,
priority: form.priority,
rate_multiplier: form.rate_multiplier,
group_ids: form.group_ids,
@@ -3533,15 +3548,21 @@ const createAccountAndFinish = async (
if (!applyTempUnschedConfig(credentials)) {
return
}
// Inject quota_limit for apikey accounts
let finalExtra = extra
if (type === 'apikey' && editQuotaLimit.value != null && editQuotaLimit.value > 0) {
finalExtra = { ...(extra || {}), quota_limit: editQuotaLimit.value }
}
await doCreateAccount({
name: form.name,
notes: form.notes,
platform,
type,
credentials,
extra,
extra: finalExtra,
proxy_id: form.proxy_id,
concurrency: form.concurrency,
load_factor: form.load_factor || undefined,
priority: form.priority,
rate_multiplier: form.rate_multiplier,
group_ids: form.group_ids,
@@ -3597,6 +3618,7 @@ const handleOpenAIExchange = async (authCode: string) => {
extra,
proxy_id: form.proxy_id,
concurrency: form.concurrency,
load_factor: form.load_factor || undefined,
priority: form.priority,
rate_multiplier: form.rate_multiplier,
group_ids: form.group_ids,
@@ -3626,6 +3648,7 @@ const handleOpenAIExchange = async (authCode: string) => {
extra: soraExtra,
proxy_id: form.proxy_id,
concurrency: form.concurrency,
load_factor: form.load_factor || undefined,
priority: form.priority,
rate_multiplier: form.rate_multiplier,
group_ids: form.group_ids,
@@ -3703,6 +3726,7 @@ const handleOpenAIValidateRT = async (refreshTokenInput: string) => {
extra,
proxy_id: form.proxy_id,
concurrency: form.concurrency,
load_factor: form.load_factor || undefined,
priority: form.priority,
rate_multiplier: form.rate_multiplier,
group_ids: form.group_ids,
@@ -3730,6 +3754,7 @@ const handleOpenAIValidateRT = async (refreshTokenInput: string) => {
extra: soraExtra,
proxy_id: form.proxy_id,
concurrency: form.concurrency,
load_factor: form.load_factor || undefined,
priority: form.priority,
rate_multiplier: form.rate_multiplier,
group_ids: form.group_ids,
@@ -3818,6 +3843,7 @@ const handleSoraValidateST = async (sessionTokenInput: string) => {
extra: soraExtra,
proxy_id: form.proxy_id,
concurrency: form.concurrency,
load_factor: form.load_factor || undefined,
priority: form.priority,
rate_multiplier: form.rate_multiplier,
group_ids: form.group_ids,
@@ -3906,6 +3932,7 @@ const handleAntigravityValidateRT = async (refreshTokenInput: string) => {
extra: {},
proxy_id: form.proxy_id,
concurrency: form.concurrency,
load_factor: form.load_factor || undefined,
priority: form.priority,
rate_multiplier: form.rate_multiplier,
group_ids: form.group_ids,
@@ -4064,8 +4091,11 @@ const handleAnthropicExchange = async (authCode: string) => {
}
// Add RPM limit settings
if (rpmLimitEnabled.value && baseRpm.value != null && baseRpm.value > 0) {
extra.base_rpm = baseRpm.value
if (rpmLimitEnabled.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
if (rpmStickyBuffer.value != null && rpmStickyBuffer.value > 0) {
extra.rpm_sticky_buffer = rpmStickyBuffer.value
@@ -4176,8 +4206,11 @@ const handleCookieAuth = async (sessionKey: string) => {
}
// Add RPM limit settings
if (rpmLimitEnabled.value && baseRpm.value != null && baseRpm.value > 0) {
extra.base_rpm = baseRpm.value
if (rpmLimitEnabled.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
if (rpmStickyBuffer.value != null && rpmStickyBuffer.value > 0) {
extra.rpm_sticky_buffer = rpmStickyBuffer.value
@@ -4223,6 +4256,7 @@ const handleCookieAuth = async (sessionKey: string) => {
extra,
proxy_id: form.proxy_id,
concurrency: form.concurrency,
load_factor: form.load_factor || undefined,
priority: form.priority,
rate_multiplier: form.rate_multiplier,
group_ids: form.group_ids,