feat(rectifier): 请求整流器增加 API Key 账号签名整流支持
新增独立开关控制 API Key 账号的签名整流功能,支持配置自定义 匹配关键词以捕获不同格式的上游错误响应。 - 新增 apikey_signature_enabled 开关(默认关闭) - 新增 apikey_signature_patterns 自定义关键词配置 - 内置签名检测规则对 API Key 账号同样生效 - 自定义关键词对完整响应体做不区分大小写匹配 - 重试二阶段检测仅做模式匹配,不重复校验开关 - Handler 层校验关键词数量(≤50)和长度(≤500) - API 响应 nil patterns 统一序列化为空数组 - OAuth/SetupToken/Upstream/Bedrock 账号行为不变
This commit is contained in:
@@ -323,6 +323,8 @@ export interface RectifierSettings {
|
||||
enabled: boolean
|
||||
thinking_signature_enabled: boolean
|
||||
thinking_budget_enabled: boolean
|
||||
apikey_signature_enabled: boolean
|
||||
apikey_signature_patterns: string[]
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4473,6 +4473,14 @@ export default {
|
||||
thinkingSignatureHint: 'Automatically strip signatures and retry when upstream returns thinking block signature validation errors',
|
||||
thinkingBudget: 'Thinking Budget Rectifier',
|
||||
thinkingBudgetHint: 'Automatically set budget to 32000 and retry when upstream returns budget_tokens constraint error (≥1024)',
|
||||
apikeySignature: 'API Key Signature Rectifier',
|
||||
apikeySignatureHint:
|
||||
'Automatically strip signatures and retry when API Key accounts receive signature-related errors (built-in patterns always apply)',
|
||||
apikeyPatterns: 'Custom Match Patterns',
|
||||
apikeyPatternsHint:
|
||||
'Additional keywords matched against the response body (case-insensitive). Built-in patterns always apply; use these for supplementary matching.',
|
||||
apikeyPatternPlaceholder: 'e.g., thinking_error',
|
||||
addPattern: 'Add Pattern',
|
||||
saved: 'Rectifier settings saved',
|
||||
saveFailed: 'Failed to save rectifier settings'
|
||||
},
|
||||
|
||||
@@ -4637,6 +4637,14 @@ export default {
|
||||
thinkingSignatureHint: '当上游返回 thinking block 签名校验错误时,自动去除签名并重试',
|
||||
thinkingBudget: 'Thinking Budget 整流',
|
||||
thinkingBudgetHint: '当上游返回 budget_tokens 约束错误(≥1024)时,自动将 budget 设为 32000 并重试',
|
||||
apikeySignature: 'API Key 签名整流',
|
||||
apikeySignatureHint:
|
||||
'当 API Key 账号的上游返回签名相关错误时,自动去除签名并重试(内置规则始终生效)',
|
||||
apikeyPatterns: '自定义匹配关键词',
|
||||
apikeyPatternsHint:
|
||||
'额外的关键词,匹配响应体中的内容(不区分大小写)。内置规则始终生效,此处用于补充额外匹配。',
|
||||
apikeyPatternPlaceholder: '例如:thinking_error 或 签名无效',
|
||||
addPattern: '添加关键词',
|
||||
saved: '整流器设置保存成功',
|
||||
saveFailed: '保存整流器设置失败'
|
||||
},
|
||||
|
||||
@@ -454,6 +454,72 @@
|
||||
</div>
|
||||
<Toggle v-model="rectifierForm.thinking_budget_enabled" />
|
||||
</div>
|
||||
|
||||
<!-- API Key Signature Rectifier -->
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<label class="text-sm font-medium text-gray-700 dark:text-gray-300">{{
|
||||
t('admin.settings.rectifier.apikeySignature')
|
||||
}}</label>
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400">
|
||||
{{ t('admin.settings.rectifier.apikeySignatureHint') }}
|
||||
</p>
|
||||
</div>
|
||||
<Toggle v-model="rectifierForm.apikey_signature_enabled" />
|
||||
</div>
|
||||
|
||||
<!-- Custom Patterns (only when apikey_signature_enabled) -->
|
||||
<div
|
||||
v-if="rectifierForm.apikey_signature_enabled"
|
||||
class="ml-4 space-y-3 border-l-2 border-gray-200 pl-4 dark:border-dark-600"
|
||||
>
|
||||
<div>
|
||||
<label class="text-sm font-medium text-gray-700 dark:text-gray-300">{{
|
||||
t('admin.settings.rectifier.apikeyPatterns')
|
||||
}}</label>
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400">
|
||||
{{ t('admin.settings.rectifier.apikeyPatternsHint') }}
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
v-for="(_, index) in rectifierForm.apikey_signature_patterns"
|
||||
:key="index"
|
||||
class="flex items-center gap-2"
|
||||
>
|
||||
<input
|
||||
v-model="rectifierForm.apikey_signature_patterns[index]"
|
||||
type="text"
|
||||
class="input input-sm flex-1"
|
||||
:placeholder="t('admin.settings.rectifier.apikeyPatternPlaceholder')"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
@click="rectifierForm.apikey_signature_patterns.splice(index, 1)"
|
||||
class="btn btn-ghost btn-xs text-red-500 hover:text-red-700"
|
||||
>
|
||||
<svg
|
||||
class="h-4 w-4"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M6 18L18 6M6 6l12 12"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
@click="rectifierForm.apikey_signature_patterns.push('')"
|
||||
class="btn btn-ghost btn-xs text-primary-600 dark:text-primary-400"
|
||||
>
|
||||
+ {{ t('admin.settings.rectifier.addPattern') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Save Button -->
|
||||
@@ -2010,7 +2076,9 @@ const rectifierSaving = ref(false)
|
||||
const rectifierForm = reactive({
|
||||
enabled: true,
|
||||
thinking_signature_enabled: true,
|
||||
thinking_budget_enabled: true
|
||||
thinking_budget_enabled: true,
|
||||
apikey_signature_enabled: false,
|
||||
apikey_signature_patterns: [] as string[]
|
||||
})
|
||||
|
||||
// Beta Policy 状态
|
||||
@@ -2626,6 +2694,10 @@ async function loadRectifierSettings() {
|
||||
try {
|
||||
const settings = await adminAPI.settings.getRectifierSettings()
|
||||
Object.assign(rectifierForm, settings)
|
||||
// 确保 patterns 是数组(旧数据可能为 null)
|
||||
if (!Array.isArray(rectifierForm.apikey_signature_patterns)) {
|
||||
rectifierForm.apikey_signature_patterns = []
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('Failed to load rectifier settings:', error)
|
||||
} finally {
|
||||
@@ -2639,9 +2711,16 @@ async function saveRectifierSettings() {
|
||||
const updated = await adminAPI.settings.updateRectifierSettings({
|
||||
enabled: rectifierForm.enabled,
|
||||
thinking_signature_enabled: rectifierForm.thinking_signature_enabled,
|
||||
thinking_budget_enabled: rectifierForm.thinking_budget_enabled
|
||||
thinking_budget_enabled: rectifierForm.thinking_budget_enabled,
|
||||
apikey_signature_enabled: rectifierForm.apikey_signature_enabled,
|
||||
apikey_signature_patterns: rectifierForm.apikey_signature_patterns.filter(
|
||||
(p) => p.trim() !== ''
|
||||
)
|
||||
})
|
||||
Object.assign(rectifierForm, updated)
|
||||
if (!Array.isArray(rectifierForm.apikey_signature_patterns)) {
|
||||
rectifierForm.apikey_signature_patterns = []
|
||||
}
|
||||
appStore.showSuccess(t('admin.settings.rectifier.saved'))
|
||||
} catch (error: any) {
|
||||
appStore.showError(
|
||||
|
||||
Reference in New Issue
Block a user