feat: Anthropic oauth/setup-token账号支持自定义转发URL
This commit is contained in:
@@ -268,6 +268,14 @@ func AccountFromServiceShallow(a *service.Account) *Account {
|
|||||||
target := a.GetCacheTTLOverrideTarget()
|
target := a.GetCacheTTLOverrideTarget()
|
||||||
out.CacheTTLOverrideTarget = &target
|
out.CacheTTLOverrideTarget = &target
|
||||||
}
|
}
|
||||||
|
// 自定义 Base URL 中继转发
|
||||||
|
if a.IsCustomBaseURLEnabled() {
|
||||||
|
enabled := true
|
||||||
|
out.CustomBaseURLEnabled = &enabled
|
||||||
|
if customURL := a.GetCustomBaseURL(); customURL != "" {
|
||||||
|
out.CustomBaseURL = &customURL
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 提取账号配额限制(apikey / bedrock 类型有效)
|
// 提取账号配额限制(apikey / bedrock 类型有效)
|
||||||
|
|||||||
@@ -198,6 +198,10 @@ type Account struct {
|
|||||||
CacheTTLOverrideEnabled *bool `json:"cache_ttl_override_enabled,omitempty"`
|
CacheTTLOverrideEnabled *bool `json:"cache_ttl_override_enabled,omitempty"`
|
||||||
CacheTTLOverrideTarget *string `json:"cache_ttl_override_target,omitempty"`
|
CacheTTLOverrideTarget *string `json:"cache_ttl_override_target,omitempty"`
|
||||||
|
|
||||||
|
// 自定义 Base URL 中继转发(仅 Anthropic OAuth/SetupToken 账号有效)
|
||||||
|
CustomBaseURLEnabled *bool `json:"custom_base_url_enabled,omitempty"`
|
||||||
|
CustomBaseURL *string `json:"custom_base_url,omitempty"`
|
||||||
|
|
||||||
// API Key 账号配额限制
|
// API Key 账号配额限制
|
||||||
QuotaLimit *float64 `json:"quota_limit,omitempty"`
|
QuotaLimit *float64 `json:"quota_limit,omitempty"`
|
||||||
QuotaUsed *float64 `json:"quota_used,omitempty"`
|
QuotaUsed *float64 `json:"quota_used,omitempty"`
|
||||||
|
|||||||
@@ -1229,6 +1229,28 @@ func (a *Account) IsSessionIDMaskingEnabled() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsCustomBaseURLEnabled 检查是否启用自定义 base URL 中继转发
|
||||||
|
// 仅适用于 Anthropic OAuth/SetupToken 类型账号
|
||||||
|
func (a *Account) IsCustomBaseURLEnabled() bool {
|
||||||
|
if !a.IsAnthropicOAuthOrSetupToken() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if a.Extra == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if v, ok := a.Extra["custom_base_url_enabled"]; ok {
|
||||||
|
if enabled, ok := v.(bool); ok {
|
||||||
|
return enabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCustomBaseURL 返回自定义中继服务的 base URL
|
||||||
|
func (a *Account) GetCustomBaseURL() string {
|
||||||
|
return a.GetExtraString("custom_base_url")
|
||||||
|
}
|
||||||
|
|
||||||
// IsCacheTTLOverrideEnabled 检查是否启用缓存 TTL 强制替换
|
// IsCacheTTLOverrideEnabled 检查是否启用缓存 TTL 强制替换
|
||||||
// 仅适用于 Anthropic OAuth/SetupToken 类型账号
|
// 仅适用于 Anthropic OAuth/SetupToken 类型账号
|
||||||
// 启用后将所有 cache creation tokens 归入指定的 TTL 类型(5m 或 1h)
|
// 启用后将所有 cache creation tokens 归入指定的 TTL 类型(5m 或 1h)
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"log/slog"
|
"log/slog"
|
||||||
mathrand "math/rand"
|
mathrand "math/rand"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
@@ -4150,10 +4151,12 @@ func (s *GatewayService) Forward(ctx context.Context, c *gin.Context, account *A
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取代理URL
|
// 获取代理URL(自定义 base URL 模式下,proxy 通过 buildCustomRelayURL 作为查询参数传递)
|
||||||
proxyURL := ""
|
proxyURL := ""
|
||||||
if account.ProxyID != nil && account.Proxy != nil {
|
if account.ProxyID != nil && account.Proxy != nil {
|
||||||
proxyURL = account.Proxy.URL()
|
if !account.IsCustomBaseURLEnabled() || account.GetCustomBaseURL() == "" {
|
||||||
|
proxyURL = account.Proxy.URL()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解析 TLS 指纹 profile(同一请求生命周期内不变,避免重试循环中重复解析)
|
// 解析 TLS 指纹 profile(同一请求生命周期内不变,避免重试循环中重复解析)
|
||||||
@@ -5628,6 +5631,16 @@ func (s *GatewayService) buildUpstreamRequest(ctx context.Context, c *gin.Contex
|
|||||||
}
|
}
|
||||||
targetURL = validatedURL + "/v1/messages?beta=true"
|
targetURL = validatedURL + "/v1/messages?beta=true"
|
||||||
}
|
}
|
||||||
|
} else if account.IsCustomBaseURLEnabled() {
|
||||||
|
customURL := account.GetCustomBaseURL()
|
||||||
|
if customURL == "" {
|
||||||
|
return nil, fmt.Errorf("custom_base_url is enabled but not configured for account %d", account.ID)
|
||||||
|
}
|
||||||
|
validatedURL, err := s.validateUpstreamBaseURL(customURL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
targetURL = s.buildCustomRelayURL(validatedURL, "/v1/messages", account)
|
||||||
}
|
}
|
||||||
|
|
||||||
clientHeaders := http.Header{}
|
clientHeaders := http.Header{}
|
||||||
@@ -8063,10 +8076,12 @@ func (s *GatewayService) ForwardCountTokens(ctx context.Context, c *gin.Context,
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取代理URL
|
// 获取代理URL(自定义 base URL 模式下,proxy 通过 buildCustomRelayURL 作为查询参数传递)
|
||||||
proxyURL := ""
|
proxyURL := ""
|
||||||
if account.ProxyID != nil && account.Proxy != nil {
|
if account.ProxyID != nil && account.Proxy != nil {
|
||||||
proxyURL = account.Proxy.URL()
|
if !account.IsCustomBaseURLEnabled() || account.GetCustomBaseURL() == "" {
|
||||||
|
proxyURL = account.Proxy.URL()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 发送请求
|
// 发送请求
|
||||||
@@ -8345,6 +8360,16 @@ func (s *GatewayService) buildCountTokensRequest(ctx context.Context, c *gin.Con
|
|||||||
}
|
}
|
||||||
targetURL = validatedURL + "/v1/messages/count_tokens?beta=true"
|
targetURL = validatedURL + "/v1/messages/count_tokens?beta=true"
|
||||||
}
|
}
|
||||||
|
} else if account.IsCustomBaseURLEnabled() {
|
||||||
|
customURL := account.GetCustomBaseURL()
|
||||||
|
if customURL == "" {
|
||||||
|
return nil, fmt.Errorf("custom_base_url is enabled but not configured for account %d", account.ID)
|
||||||
|
}
|
||||||
|
validatedURL, err := s.validateUpstreamBaseURL(customURL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
targetURL = s.buildCustomRelayURL(validatedURL, "/v1/messages/count_tokens", account)
|
||||||
}
|
}
|
||||||
|
|
||||||
clientHeaders := http.Header{}
|
clientHeaders := http.Header{}
|
||||||
@@ -8471,6 +8496,19 @@ func (s *GatewayService) countTokensError(c *gin.Context, status int, errType, m
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// buildCustomRelayURL 构建自定义中继转发 URL
|
||||||
|
// 在 path 后附加 beta=true 和可选的 proxy 查询参数
|
||||||
|
func (s *GatewayService) buildCustomRelayURL(baseURL, path string, account *Account) string {
|
||||||
|
u := strings.TrimRight(baseURL, "/") + path + "?beta=true"
|
||||||
|
if account.ProxyID != nil && account.Proxy != nil {
|
||||||
|
proxyURL := account.Proxy.URL()
|
||||||
|
if proxyURL != "" {
|
||||||
|
u += "&proxy=" + url.QueryEscape(proxyURL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
func (s *GatewayService) validateUpstreamBaseURL(raw string) (string, error) {
|
func (s *GatewayService) validateUpstreamBaseURL(raw string) (string, error) {
|
||||||
if s.cfg != nil && !s.cfg.Security.URLAllowlist.Enabled {
|
if s.cfg != nil && !s.cfg.Security.URLAllowlist.Enabled {
|
||||||
normalized, err := urlvalidator.ValidateURLFormat(raw, s.cfg.Security.URLAllowlist.AllowInsecureHTTP)
|
normalized, err := urlvalidator.ValidateURLFormat(raw, s.cfg.Security.URLAllowlist.AllowInsecureHTTP)
|
||||||
|
|||||||
@@ -2245,6 +2245,41 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Custom Base URL Relay -->
|
||||||
|
<div class="rounded-lg border border-gray-200 p-4 dark:border-dark-600">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<label class="input-label mb-0">{{ t('admin.accounts.quotaControl.customBaseUrl.label') }}</label>
|
||||||
|
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||||
|
{{ t('admin.accounts.quotaControl.customBaseUrl.hint') }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
@click="customBaseUrlEnabled = !customBaseUrlEnabled"
|
||||||
|
:class="[
|
||||||
|
'relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2',
|
||||||
|
customBaseUrlEnabled ? 'bg-primary-600' : 'bg-gray-200 dark:bg-dark-600'
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
:class="[
|
||||||
|
'pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out',
|
||||||
|
customBaseUrlEnabled ? 'translate-x-5' : 'translate-x-0'
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div v-if="customBaseUrlEnabled" class="mt-3">
|
||||||
|
<input
|
||||||
|
v-model="customBaseUrl"
|
||||||
|
type="text"
|
||||||
|
class="input"
|
||||||
|
:placeholder="t('admin.accounts.quotaControl.customBaseUrl.urlHint')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
@@ -3095,6 +3130,8 @@ const tlsFingerprintProfiles = ref<{ id: number; name: string }[]>([])
|
|||||||
const sessionIdMaskingEnabled = ref(false)
|
const sessionIdMaskingEnabled = ref(false)
|
||||||
const cacheTTLOverrideEnabled = ref(false)
|
const cacheTTLOverrideEnabled = ref(false)
|
||||||
const cacheTTLOverrideTarget = ref<string>('5m')
|
const cacheTTLOverrideTarget = ref<string>('5m')
|
||||||
|
const customBaseUrlEnabled = ref(false)
|
||||||
|
const customBaseUrl = ref('')
|
||||||
|
|
||||||
// Gemini tier selection (used as fallback when auto-detection is unavailable/fails)
|
// Gemini tier selection (used as fallback when auto-detection is unavailable/fails)
|
||||||
const geminiTierGoogleOne = ref<'google_one_free' | 'google_ai_pro' | 'google_ai_ultra'>('google_one_free')
|
const geminiTierGoogleOne = ref<'google_one_free' | 'google_ai_pro' | 'google_ai_ultra'>('google_one_free')
|
||||||
@@ -3765,6 +3802,8 @@ const resetForm = () => {
|
|||||||
sessionIdMaskingEnabled.value = false
|
sessionIdMaskingEnabled.value = false
|
||||||
cacheTTLOverrideEnabled.value = false
|
cacheTTLOverrideEnabled.value = false
|
||||||
cacheTTLOverrideTarget.value = '5m'
|
cacheTTLOverrideTarget.value = '5m'
|
||||||
|
customBaseUrlEnabled.value = false
|
||||||
|
customBaseUrl.value = ''
|
||||||
allowOverages.value = false
|
allowOverages.value = false
|
||||||
antigravityAccountType.value = 'oauth'
|
antigravityAccountType.value = 'oauth'
|
||||||
upstreamBaseUrl.value = ''
|
upstreamBaseUrl.value = ''
|
||||||
@@ -4856,6 +4895,12 @@ const handleAnthropicExchange = async (authCode: string) => {
|
|||||||
extra.cache_ttl_override_target = cacheTTLOverrideTarget.value
|
extra.cache_ttl_override_target = cacheTTLOverrideTarget.value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add custom base URL settings
|
||||||
|
if (customBaseUrlEnabled.value && customBaseUrl.value.trim()) {
|
||||||
|
extra.custom_base_url_enabled = true
|
||||||
|
extra.custom_base_url = customBaseUrl.value.trim()
|
||||||
|
}
|
||||||
|
|
||||||
const credentials: Record<string, unknown> = { ...tokenInfo }
|
const credentials: Record<string, unknown> = { ...tokenInfo }
|
||||||
applyInterceptWarmup(credentials, interceptWarmupRequests.value, 'create')
|
applyInterceptWarmup(credentials, interceptWarmupRequests.value, 'create')
|
||||||
await createAccountAndFinish(form.platform, addMethod.value as AccountType, credentials, extra)
|
await createAccountAndFinish(form.platform, addMethod.value as AccountType, credentials, extra)
|
||||||
@@ -4974,6 +5019,12 @@ const handleCookieAuth = async (sessionKey: string) => {
|
|||||||
extra.cache_ttl_override_target = cacheTTLOverrideTarget.value
|
extra.cache_ttl_override_target = cacheTTLOverrideTarget.value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add custom base URL settings
|
||||||
|
if (customBaseUrlEnabled.value && customBaseUrl.value.trim()) {
|
||||||
|
extra.custom_base_url_enabled = true
|
||||||
|
extra.custom_base_url = customBaseUrl.value.trim()
|
||||||
|
}
|
||||||
|
|
||||||
const accountName = keys.length > 1 ? `${form.name} #${i + 1}` : form.name
|
const accountName = keys.length > 1 ? `${form.name} #${i + 1}` : form.name
|
||||||
|
|
||||||
const credentials: Record<string, unknown> = { ...tokenInfo }
|
const credentials: Record<string, unknown> = { ...tokenInfo }
|
||||||
|
|||||||
@@ -1580,6 +1580,41 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Custom Base URL Relay -->
|
||||||
|
<div class="rounded-lg border border-gray-200 p-4 dark:border-dark-600">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<label class="input-label mb-0">{{ t('admin.accounts.quotaControl.customBaseUrl.label') }}</label>
|
||||||
|
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||||
|
{{ t('admin.accounts.quotaControl.customBaseUrl.hint') }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
@click="customBaseUrlEnabled = !customBaseUrlEnabled"
|
||||||
|
:class="[
|
||||||
|
'relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2',
|
||||||
|
customBaseUrlEnabled ? 'bg-primary-600' : 'bg-gray-200 dark:bg-dark-600'
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
:class="[
|
||||||
|
'pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out',
|
||||||
|
customBaseUrlEnabled ? 'translate-x-5' : 'translate-x-0'
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div v-if="customBaseUrlEnabled" class="mt-3">
|
||||||
|
<input
|
||||||
|
v-model="customBaseUrl"
|
||||||
|
type="text"
|
||||||
|
class="input"
|
||||||
|
:placeholder="t('admin.accounts.quotaControl.customBaseUrl.urlHint')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="border-t border-gray-200 pt-4 dark:border-dark-600">
|
<div class="border-t border-gray-200 pt-4 dark:border-dark-600">
|
||||||
@@ -1854,6 +1889,8 @@ const tlsFingerprintProfiles = ref<{ id: number; name: string }[]>([])
|
|||||||
const sessionIdMaskingEnabled = ref(false)
|
const sessionIdMaskingEnabled = ref(false)
|
||||||
const cacheTTLOverrideEnabled = ref(false)
|
const cacheTTLOverrideEnabled = ref(false)
|
||||||
const cacheTTLOverrideTarget = ref<string>('5m')
|
const cacheTTLOverrideTarget = ref<string>('5m')
|
||||||
|
const customBaseUrlEnabled = ref(false)
|
||||||
|
const customBaseUrl = ref('')
|
||||||
|
|
||||||
// OpenAI 自动透传开关(OAuth/API Key)
|
// OpenAI 自动透传开关(OAuth/API Key)
|
||||||
const openaiPassthroughEnabled = ref(false)
|
const openaiPassthroughEnabled = ref(false)
|
||||||
@@ -2482,6 +2519,8 @@ function loadQuotaControlSettings(account: Account) {
|
|||||||
sessionIdMaskingEnabled.value = false
|
sessionIdMaskingEnabled.value = false
|
||||||
cacheTTLOverrideEnabled.value = false
|
cacheTTLOverrideEnabled.value = false
|
||||||
cacheTTLOverrideTarget.value = '5m'
|
cacheTTLOverrideTarget.value = '5m'
|
||||||
|
customBaseUrlEnabled.value = false
|
||||||
|
customBaseUrl.value = ''
|
||||||
|
|
||||||
// Only applies to Anthropic OAuth/SetupToken accounts
|
// Only applies to Anthropic OAuth/SetupToken accounts
|
||||||
if (account.platform !== 'anthropic' || (account.type !== 'oauth' && account.type !== 'setup-token')) {
|
if (account.platform !== 'anthropic' || (account.type !== 'oauth' && account.type !== 'setup-token')) {
|
||||||
@@ -2528,6 +2567,12 @@ function loadQuotaControlSettings(account: Account) {
|
|||||||
cacheTTLOverrideEnabled.value = true
|
cacheTTLOverrideEnabled.value = true
|
||||||
cacheTTLOverrideTarget.value = account.cache_ttl_override_target || '5m'
|
cacheTTLOverrideTarget.value = account.cache_ttl_override_target || '5m'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load custom base URL setting
|
||||||
|
if (account.custom_base_url_enabled === true) {
|
||||||
|
customBaseUrlEnabled.value = true
|
||||||
|
customBaseUrl.value = account.custom_base_url || ''
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatTempUnschedKeywords(value: unknown) {
|
function formatTempUnschedKeywords(value: unknown) {
|
||||||
@@ -2980,6 +3025,15 @@ const handleSubmit = async () => {
|
|||||||
delete newExtra.cache_ttl_override_target
|
delete newExtra.cache_ttl_override_target
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Custom base URL relay setting
|
||||||
|
if (customBaseUrlEnabled.value && customBaseUrl.value.trim()) {
|
||||||
|
newExtra.custom_base_url_enabled = true
|
||||||
|
newExtra.custom_base_url = customBaseUrl.value.trim()
|
||||||
|
} else {
|
||||||
|
delete newExtra.custom_base_url_enabled
|
||||||
|
delete newExtra.custom_base_url
|
||||||
|
}
|
||||||
|
|
||||||
updatePayload.extra = newExtra
|
updatePayload.extra = newExtra
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2318,6 +2318,11 @@ export default {
|
|||||||
target: 'Target TTL',
|
target: 'Target TTL',
|
||||||
targetHint: 'Select the TTL tier for billing'
|
targetHint: 'Select the TTL tier for billing'
|
||||||
},
|
},
|
||||||
|
customBaseUrl: {
|
||||||
|
label: 'Custom Relay URL',
|
||||||
|
hint: 'Forward requests to a custom relay service. Proxy URL will be passed as a query parameter.',
|
||||||
|
urlHint: 'Relay service URL (e.g., https://relay.example.com)',
|
||||||
|
},
|
||||||
clientAffinity: {
|
clientAffinity: {
|
||||||
label: 'Client Affinity Scheduling',
|
label: 'Client Affinity Scheduling',
|
||||||
hint: 'When enabled, new sessions prefer accounts previously used by this client to reduce account switching'
|
hint: 'When enabled, new sessions prefer accounts previously used by this client to reduce account switching'
|
||||||
|
|||||||
@@ -2462,6 +2462,11 @@ export default {
|
|||||||
target: '目标 TTL',
|
target: '目标 TTL',
|
||||||
targetHint: '选择计费使用的 TTL 类型'
|
targetHint: '选择计费使用的 TTL 类型'
|
||||||
},
|
},
|
||||||
|
customBaseUrl: {
|
||||||
|
label: '自定义转发地址',
|
||||||
|
hint: '启用后将请求转发到自定义中继服务,代理地址将作为 URL 参数传递给中继服务',
|
||||||
|
urlHint: '中继服务地址(如 https://relay.example.com)',
|
||||||
|
},
|
||||||
clientAffinity: {
|
clientAffinity: {
|
||||||
label: '客户端亲和调度',
|
label: '客户端亲和调度',
|
||||||
hint: '启用后,新会话会优先调度到该客户端之前使用过的账号,避免频繁切换账号'
|
hint: '启用后,新会话会优先调度到该客户端之前使用过的账号,避免频繁切换账号'
|
||||||
|
|||||||
@@ -734,6 +734,10 @@ export interface Account {
|
|||||||
cache_ttl_override_enabled?: boolean | null
|
cache_ttl_override_enabled?: boolean | null
|
||||||
cache_ttl_override_target?: string | null
|
cache_ttl_override_target?: string | null
|
||||||
|
|
||||||
|
// 自定义 Base URL 中继转发(仅 Anthropic OAuth/SetupToken 账号有效)
|
||||||
|
custom_base_url_enabled?: boolean | null
|
||||||
|
custom_base_url?: string | null
|
||||||
|
|
||||||
// 客户端亲和调度(仅 Anthropic/Antigravity 平台有效)
|
// 客户端亲和调度(仅 Anthropic/Antigravity 平台有效)
|
||||||
// 启用后新会话会优先调度到客户端之前使用过的账号
|
// 启用后新会话会优先调度到客户端之前使用过的账号
|
||||||
client_affinity_enabled?: boolean | null
|
client_affinity_enabled?: boolean | null
|
||||||
|
|||||||
Reference in New Issue
Block a user