feat: Anthropic平台可配置 anthropic-beta 策略

This commit is contained in:
shaw
2026-03-10 11:14:17 +08:00
parent ac6bde7a98
commit 00a0a12138
14 changed files with 588 additions and 29 deletions

View File

@@ -308,6 +308,49 @@ export async function updateRectifierSettings(
return data
}
// ==================== Beta Policy Settings ====================
/**
* Beta policy rule interface
*/
export interface BetaPolicyRule {
beta_token: string
action: 'pass' | 'filter' | 'block'
scope: 'all' | 'oauth' | 'apikey'
error_message?: string
}
/**
* Beta policy settings interface
*/
export interface BetaPolicySettings {
rules: BetaPolicyRule[]
}
/**
* Get beta policy settings
* @returns Beta policy settings
*/
export async function getBetaPolicySettings(): Promise<BetaPolicySettings> {
const { data } = await apiClient.get<BetaPolicySettings>('/admin/settings/beta-policy')
return data
}
/**
* Update beta policy settings
* @param settings - Beta policy settings to update
* @returns Updated settings
*/
export async function updateBetaPolicySettings(
settings: BetaPolicySettings
): Promise<BetaPolicySettings> {
const { data } = await apiClient.put<BetaPolicySettings>(
'/admin/settings/beta-policy',
settings
)
return data
}
// ==================== Sora S3 Settings ====================
export interface SoraS3Settings {
@@ -456,6 +499,8 @@ export const settingsAPI = {
updateStreamTimeoutSettings,
getRectifierSettings,
updateRectifierSettings,
getBetaPolicySettings,
updateBetaPolicySettings,
getSoraS3Settings,
updateSoraS3Settings,
testSoraS3Connection,

View File

@@ -4043,6 +4043,23 @@ export default {
saved: 'Rectifier settings saved',
saveFailed: 'Failed to save rectifier settings'
},
betaPolicy: {
title: 'Beta Policy',
description: 'How to handle Beta features when configuring the forwarding of Anthropic API requests. Applicable only to the /v1/messages endpoint.',
action: 'Action',
actionPass: 'Pass (transparent)',
actionFilter: 'Filter (remove)',
actionBlock: 'Block (reject)',
scope: 'Scope',
scopeAll: 'All accounts',
scopeOAuth: 'OAuth only',
scopeAPIKey: 'API Key only',
errorMessage: 'Error message',
errorMessagePlaceholder: 'Custom error message when blocked',
errorMessageHint: 'Leave empty for default message',
saved: 'Beta policy settings saved',
saveFailed: 'Failed to save beta policy settings'
},
saveSettings: 'Save Settings',
saving: 'Saving...',
settingsSaved: 'Settings saved successfully',

View File

@@ -4216,6 +4216,23 @@ export default {
saved: '整流器设置保存成功',
saveFailed: '保存整流器设置失败'
},
betaPolicy: {
title: 'Beta 策略',
description: '配置转发 Anthropic API 请求时如何处理 Beta 特性。仅适用于 /v1/messages 接口。',
action: '处理方式',
actionPass: '透传(不处理)',
actionFilter: '过滤(移除)',
actionBlock: '拦截(拒绝请求)',
scope: '生效范围',
scopeAll: '全部账号',
scopeOAuth: '仅 OAuth 账号',
scopeAPIKey: '仅 API Key 账号',
errorMessage: '错误消息',
errorMessagePlaceholder: '拦截时返回的自定义错误消息',
errorMessageHint: '留空则使用默认错误消息',
saved: 'Beta 策略设置保存成功',
saveFailed: '保存 Beta 策略设置失败'
},
saveSettings: '保存设置',
saving: '保存中...',
settingsSaved: '设置保存成功',

View File

@@ -405,6 +405,117 @@
</template>
</div>
</div>
<!-- Beta Policy Settings -->
<div class="card">
<div class="border-b border-gray-100 px-6 py-4 dark:border-dark-700">
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">
{{ t('admin.settings.betaPolicy.title') }}
</h2>
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
{{ t('admin.settings.betaPolicy.description') }}
</p>
</div>
<div class="space-y-5 p-6">
<!-- Loading State -->
<div v-if="betaPolicyLoading" class="flex items-center gap-2 text-gray-500">
<div class="h-4 w-4 animate-spin rounded-full border-b-2 border-primary-600"></div>
{{ t('common.loading') }}
</div>
<template v-else>
<!-- Rule Cards -->
<div
v-for="rule in betaPolicyForm.rules"
:key="rule.beta_token"
class="rounded-lg border border-gray-200 p-4 dark:border-dark-600"
>
<div class="mb-3 flex items-center gap-2">
<span class="text-sm font-medium text-gray-900 dark:text-white">
{{ getBetaDisplayName(rule.beta_token) }}
</span>
<span class="rounded bg-gray-100 px-2 py-0.5 text-xs text-gray-500 dark:bg-dark-700 dark:text-gray-400">
{{ rule.beta_token }}
</span>
</div>
<div class="grid grid-cols-2 gap-4">
<!-- Action -->
<div>
<label class="mb-1 block text-xs font-medium text-gray-600 dark:text-gray-400">
{{ t('admin.settings.betaPolicy.action') }}
</label>
<Select
:modelValue="rule.action"
@update:modelValue="rule.action = $event as any"
:options="betaPolicyActionOptions"
/>
</div>
<!-- Scope -->
<div>
<label class="mb-1 block text-xs font-medium text-gray-600 dark:text-gray-400">
{{ t('admin.settings.betaPolicy.scope') }}
</label>
<Select
:modelValue="rule.scope"
@update:modelValue="rule.scope = $event as any"
:options="betaPolicyScopeOptions"
/>
</div>
</div>
<!-- Error Message (only when action=block) -->
<div v-if="rule.action === 'block'" class="mt-3">
<label class="mb-1 block text-xs font-medium text-gray-600 dark:text-gray-400">
{{ t('admin.settings.betaPolicy.errorMessage') }}
</label>
<input
v-model="rule.error_message"
type="text"
class="input"
:placeholder="t('admin.settings.betaPolicy.errorMessagePlaceholder')"
/>
<p class="mt-1 text-xs text-gray-400 dark:text-gray-500">
{{ t('admin.settings.betaPolicy.errorMessageHint') }}
</p>
</div>
</div>
<!-- Save Button -->
<div class="flex justify-end border-t border-gray-100 pt-4 dark:border-dark-700">
<button
type="button"
@click="saveBetaPolicySettings"
:disabled="betaPolicySaving"
class="btn btn-primary btn-sm"
>
<svg
v-if="betaPolicySaving"
class="mr-1 h-4 w-4 animate-spin"
fill="none"
viewBox="0 0 24 24"
>
<circle
class="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
stroke-width="4"
></circle>
<path
class="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
></path>
</svg>
{{ betaPolicySaving ? t('common.saving') : t('common.save') }}
</button>
</div>
</template>
</div>
</div>
</div><!-- /Tab: Gateway -->
<!-- Tab: Security Registration, Turnstile, LinuxDo -->
@@ -1627,6 +1738,18 @@ const rectifierForm = reactive({
thinking_budget_enabled: true
})
// Beta Policy 状态
const betaPolicyLoading = ref(true)
const betaPolicySaving = ref(false)
const betaPolicyForm = reactive({
rules: [] as Array<{
beta_token: string
action: 'pass' | 'filter' | 'block'
scope: 'all' | 'oauth' | 'apikey'
error_message?: string
}>
})
interface DefaultSubscriptionGroupOption {
value: number
label: string
@@ -2165,12 +2288,64 @@ async function saveRectifierSettings() {
}
}
const betaPolicyActionOptions = computed(() => [
{ value: 'pass', label: t('admin.settings.betaPolicy.actionPass') },
{ value: 'filter', label: t('admin.settings.betaPolicy.actionFilter') },
{ value: 'block', label: t('admin.settings.betaPolicy.actionBlock') }
])
const betaPolicyScopeOptions = computed(() => [
{ value: 'all', label: t('admin.settings.betaPolicy.scopeAll') },
{ value: 'oauth', label: t('admin.settings.betaPolicy.scopeOAuth') },
{ value: 'apikey', label: t('admin.settings.betaPolicy.scopeAPIKey') }
])
// Beta Policy 方法
const betaDisplayNames: Record<string, string> = {
'fast-mode-2026-02-01': 'Fast Mode',
'context-1m-2025-08-07': 'Context 1M'
}
function getBetaDisplayName(token: string): string {
return betaDisplayNames[token] || token
}
async function loadBetaPolicySettings() {
betaPolicyLoading.value = true
try {
const settings = await adminAPI.settings.getBetaPolicySettings()
betaPolicyForm.rules = settings.rules
} catch (error: any) {
console.error('Failed to load beta policy settings:', error)
} finally {
betaPolicyLoading.value = false
}
}
async function saveBetaPolicySettings() {
betaPolicySaving.value = true
try {
const updated = await adminAPI.settings.updateBetaPolicySettings({
rules: betaPolicyForm.rules
})
betaPolicyForm.rules = updated.rules
appStore.showSuccess(t('admin.settings.betaPolicy.saved'))
} catch (error: any) {
appStore.showError(
t('admin.settings.betaPolicy.saveFailed') + ': ' + (error.message || t('common.unknownError'))
)
} finally {
betaPolicySaving.value = false
}
}
onMounted(() => {
loadSettings()
loadSubscriptionGroups()
loadAdminApiKey()
loadStreamTimeoutSettings()
loadRectifierSettings()
loadBetaPolicySettings()
})
</script>