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:
parent
ce96527dd9
commit
d571f300e5
@ -1594,18 +1594,26 @@ func (h *SettingHandler) GetRectifierSettings(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
patterns := settings.APIKeySignaturePatterns
|
||||||
|
if patterns == nil {
|
||||||
|
patterns = []string{}
|
||||||
|
}
|
||||||
response.Success(c, dto.RectifierSettings{
|
response.Success(c, dto.RectifierSettings{
|
||||||
Enabled: settings.Enabled,
|
Enabled: settings.Enabled,
|
||||||
ThinkingSignatureEnabled: settings.ThinkingSignatureEnabled,
|
ThinkingSignatureEnabled: settings.ThinkingSignatureEnabled,
|
||||||
ThinkingBudgetEnabled: settings.ThinkingBudgetEnabled,
|
ThinkingBudgetEnabled: settings.ThinkingBudgetEnabled,
|
||||||
|
APIKeySignatureEnabled: settings.APIKeySignatureEnabled,
|
||||||
|
APIKeySignaturePatterns: patterns,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateRectifierSettingsRequest 更新整流器配置请求
|
// UpdateRectifierSettingsRequest 更新整流器配置请求
|
||||||
type UpdateRectifierSettingsRequest struct {
|
type UpdateRectifierSettingsRequest struct {
|
||||||
Enabled bool `json:"enabled"`
|
Enabled bool `json:"enabled"`
|
||||||
ThinkingSignatureEnabled bool `json:"thinking_signature_enabled"`
|
ThinkingSignatureEnabled bool `json:"thinking_signature_enabled"`
|
||||||
ThinkingBudgetEnabled bool `json:"thinking_budget_enabled"`
|
ThinkingBudgetEnabled bool `json:"thinking_budget_enabled"`
|
||||||
|
APIKeySignatureEnabled bool `json:"apikey_signature_enabled"`
|
||||||
|
APIKeySignaturePatterns []string `json:"apikey_signature_patterns"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateRectifierSettings 更新请求整流器配置
|
// UpdateRectifierSettings 更新请求整流器配置
|
||||||
@ -1617,10 +1625,32 @@ func (h *SettingHandler) UpdateRectifierSettings(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 校验并清理自定义匹配关键词
|
||||||
|
const maxPatterns = 50
|
||||||
|
const maxPatternLen = 500
|
||||||
|
if len(req.APIKeySignaturePatterns) > maxPatterns {
|
||||||
|
response.BadRequest(c, "Too many signature patterns (max 50)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var cleanedPatterns []string
|
||||||
|
for _, p := range req.APIKeySignaturePatterns {
|
||||||
|
p = strings.TrimSpace(p)
|
||||||
|
if p == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if len(p) > maxPatternLen {
|
||||||
|
response.BadRequest(c, "Signature pattern too long (max 500 characters)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cleanedPatterns = append(cleanedPatterns, p)
|
||||||
|
}
|
||||||
|
|
||||||
settings := &service.RectifierSettings{
|
settings := &service.RectifierSettings{
|
||||||
Enabled: req.Enabled,
|
Enabled: req.Enabled,
|
||||||
ThinkingSignatureEnabled: req.ThinkingSignatureEnabled,
|
ThinkingSignatureEnabled: req.ThinkingSignatureEnabled,
|
||||||
ThinkingBudgetEnabled: req.ThinkingBudgetEnabled,
|
ThinkingBudgetEnabled: req.ThinkingBudgetEnabled,
|
||||||
|
APIKeySignatureEnabled: req.APIKeySignatureEnabled,
|
||||||
|
APIKeySignaturePatterns: cleanedPatterns,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := h.settingService.SetRectifierSettings(c.Request.Context(), settings); err != nil {
|
if err := h.settingService.SetRectifierSettings(c.Request.Context(), settings); err != nil {
|
||||||
@ -1635,10 +1665,16 @@ func (h *SettingHandler) UpdateRectifierSettings(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updatedPatterns := updatedSettings.APIKeySignaturePatterns
|
||||||
|
if updatedPatterns == nil {
|
||||||
|
updatedPatterns = []string{}
|
||||||
|
}
|
||||||
response.Success(c, dto.RectifierSettings{
|
response.Success(c, dto.RectifierSettings{
|
||||||
Enabled: updatedSettings.Enabled,
|
Enabled: updatedSettings.Enabled,
|
||||||
ThinkingSignatureEnabled: updatedSettings.ThinkingSignatureEnabled,
|
ThinkingSignatureEnabled: updatedSettings.ThinkingSignatureEnabled,
|
||||||
ThinkingBudgetEnabled: updatedSettings.ThinkingBudgetEnabled,
|
ThinkingBudgetEnabled: updatedSettings.ThinkingBudgetEnabled,
|
||||||
|
APIKeySignatureEnabled: updatedSettings.APIKeySignatureEnabled,
|
||||||
|
APIKeySignaturePatterns: updatedPatterns,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -188,9 +188,11 @@ type StreamTimeoutSettings struct {
|
|||||||
|
|
||||||
// RectifierSettings 请求整流器配置 DTO
|
// RectifierSettings 请求整流器配置 DTO
|
||||||
type RectifierSettings struct {
|
type RectifierSettings struct {
|
||||||
Enabled bool `json:"enabled"`
|
Enabled bool `json:"enabled"`
|
||||||
ThinkingSignatureEnabled bool `json:"thinking_signature_enabled"`
|
ThinkingSignatureEnabled bool `json:"thinking_signature_enabled"`
|
||||||
ThinkingBudgetEnabled bool `json:"thinking_budget_enabled"`
|
ThinkingBudgetEnabled bool `json:"thinking_budget_enabled"`
|
||||||
|
APIKeySignatureEnabled bool `json:"apikey_signature_enabled"`
|
||||||
|
APIKeySignaturePatterns []string `json:"apikey_signature_patterns"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// BetaPolicyRule Beta 策略规则 DTO
|
// BetaPolicyRule Beta 策略规则 DTO
|
||||||
|
|||||||
@ -4188,7 +4188,7 @@ func (s *GatewayService) Forward(ctx context.Context, c *gin.Context, account *A
|
|||||||
if readErr == nil {
|
if readErr == nil {
|
||||||
_ = resp.Body.Close()
|
_ = resp.Body.Close()
|
||||||
|
|
||||||
if s.isThinkingBlockSignatureError(respBody) && s.settingService.IsSignatureRectifierEnabled(ctx) {
|
if s.shouldRectifySignatureError(ctx, account, respBody) {
|
||||||
appendOpsUpstreamError(c, OpsUpstreamErrorEvent{
|
appendOpsUpstreamError(c, OpsUpstreamErrorEvent{
|
||||||
Platform: account.Platform,
|
Platform: account.Platform,
|
||||||
AccountID: account.ID,
|
AccountID: account.ID,
|
||||||
@ -4243,7 +4243,7 @@ func (s *GatewayService) Forward(ctx context.Context, c *gin.Context, account *A
|
|||||||
|
|
||||||
retryRespBody, retryReadErr := io.ReadAll(io.LimitReader(retryResp.Body, 2<<20))
|
retryRespBody, retryReadErr := io.ReadAll(io.LimitReader(retryResp.Body, 2<<20))
|
||||||
_ = retryResp.Body.Close()
|
_ = retryResp.Body.Close()
|
||||||
if retryReadErr == nil && retryResp.StatusCode == 400 && s.isThinkingBlockSignatureError(retryRespBody) {
|
if retryReadErr == nil && retryResp.StatusCode == 400 && s.isSignatureErrorPattern(ctx, account, retryRespBody) {
|
||||||
appendOpsUpstreamError(c, OpsUpstreamErrorEvent{
|
appendOpsUpstreamError(c, OpsUpstreamErrorEvent{
|
||||||
Platform: account.Platform,
|
Platform: account.Platform,
|
||||||
AccountID: account.ID,
|
AccountID: account.ID,
|
||||||
@ -6145,6 +6145,59 @@ func truncateForLog(b []byte, maxBytes int) string {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// shouldRectifySignatureError 统一判断是否应触发签名整流(strip thinking blocks 并重试)。
|
||||||
|
// 根据账号类型检查对应的开关和匹配模式。
|
||||||
|
func (s *GatewayService) shouldRectifySignatureError(ctx context.Context, account *Account, respBody []byte) bool {
|
||||||
|
if account.Type == AccountTypeAPIKey {
|
||||||
|
// API Key 账号:独立开关,一次读取配置
|
||||||
|
settings, err := s.settingService.GetRectifierSettings(ctx)
|
||||||
|
if err != nil || !settings.Enabled || !settings.APIKeySignatureEnabled {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// 先检查内置模式(同 OAuth),再检查自定义关键词
|
||||||
|
if s.isThinkingBlockSignatureError(respBody) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return matchSignaturePatterns(respBody, settings.APIKeySignaturePatterns)
|
||||||
|
}
|
||||||
|
// OAuth/SetupToken/Upstream/Bedrock 等:保持原有行为(内置模式 + 原开关)
|
||||||
|
return s.isThinkingBlockSignatureError(respBody) && s.settingService.IsSignatureRectifierEnabled(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// isSignatureErrorPattern 仅做模式匹配,不检查开关。
|
||||||
|
// 用于已进入重试流程后的二阶段检测(此时开关已在首次调用时验证过)。
|
||||||
|
func (s *GatewayService) isSignatureErrorPattern(ctx context.Context, account *Account, respBody []byte) bool {
|
||||||
|
if s.isThinkingBlockSignatureError(respBody) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if account.Type == AccountTypeAPIKey {
|
||||||
|
settings, err := s.settingService.GetRectifierSettings(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return matchSignaturePatterns(respBody, settings.APIKeySignaturePatterns)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// matchSignaturePatterns 检查响应体是否匹配自定义关键词列表(不区分大小写)。
|
||||||
|
func matchSignaturePatterns(respBody []byte, patterns []string) bool {
|
||||||
|
if len(patterns) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
bodyLower := strings.ToLower(string(respBody))
|
||||||
|
for _, p := range patterns {
|
||||||
|
p = strings.TrimSpace(p)
|
||||||
|
if p == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strings.Contains(bodyLower, strings.ToLower(p)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// isThinkingBlockSignatureError 检测是否是thinking block相关错误
|
// isThinkingBlockSignatureError 检测是否是thinking block相关错误
|
||||||
// 这类错误可以通过过滤thinking blocks并重试来解决
|
// 这类错误可以通过过滤thinking blocks并重试来解决
|
||||||
func (s *GatewayService) isThinkingBlockSignatureError(respBody []byte) bool {
|
func (s *GatewayService) isThinkingBlockSignatureError(respBody []byte) bool {
|
||||||
@ -8013,7 +8066,7 @@ func (s *GatewayService) ForwardCountTokens(ctx context.Context, c *gin.Context,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 检测 thinking block 签名错误(400)并重试一次(过滤 thinking blocks)
|
// 检测 thinking block 签名错误(400)并重试一次(过滤 thinking blocks)
|
||||||
if resp.StatusCode == 400 && s.isThinkingBlockSignatureError(respBody) && s.settingService.IsSignatureRectifierEnabled(ctx) {
|
if resp.StatusCode == 400 && s.shouldRectifySignatureError(ctx, account, respBody) {
|
||||||
logger.LegacyPrintf("service.gateway", "Account %d: detected thinking block signature error on count_tokens, retrying with filtered thinking blocks", account.ID)
|
logger.LegacyPrintf("service.gateway", "Account %d: detected thinking block signature error on count_tokens, retrying with filtered thinking blocks", account.ID)
|
||||||
|
|
||||||
filteredBody := FilterThinkingBlocksForRetry(body)
|
filteredBody := FilterThinkingBlocksForRetry(body)
|
||||||
|
|||||||
@ -190,9 +190,11 @@ func DefaultStreamTimeoutSettings() *StreamTimeoutSettings {
|
|||||||
|
|
||||||
// RectifierSettings 请求整流器配置
|
// RectifierSettings 请求整流器配置
|
||||||
type RectifierSettings struct {
|
type RectifierSettings struct {
|
||||||
Enabled bool `json:"enabled"` // 总开关
|
Enabled bool `json:"enabled"` // 总开关
|
||||||
ThinkingSignatureEnabled bool `json:"thinking_signature_enabled"` // Thinking 签名整流
|
ThinkingSignatureEnabled bool `json:"thinking_signature_enabled"` // Thinking 签名整流
|
||||||
ThinkingBudgetEnabled bool `json:"thinking_budget_enabled"` // Thinking Budget 整流
|
ThinkingBudgetEnabled bool `json:"thinking_budget_enabled"` // Thinking Budget 整流
|
||||||
|
APIKeySignatureEnabled bool `json:"apikey_signature_enabled"` // API Key 签名整流开关
|
||||||
|
APIKeySignaturePatterns []string `json:"apikey_signature_patterns"` // API Key 自定义匹配关键词
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultRectifierSettings 返回默认的整流器配置(全部启用)
|
// DefaultRectifierSettings 返回默认的整流器配置(全部启用)
|
||||||
|
|||||||
@ -323,6 +323,8 @@ export interface RectifierSettings {
|
|||||||
enabled: boolean
|
enabled: boolean
|
||||||
thinking_signature_enabled: boolean
|
thinking_signature_enabled: boolean
|
||||||
thinking_budget_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',
|
thinkingSignatureHint: 'Automatically strip signatures and retry when upstream returns thinking block signature validation errors',
|
||||||
thinkingBudget: 'Thinking Budget Rectifier',
|
thinkingBudget: 'Thinking Budget Rectifier',
|
||||||
thinkingBudgetHint: 'Automatically set budget to 32000 and retry when upstream returns budget_tokens constraint error (≥1024)',
|
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',
|
saved: 'Rectifier settings saved',
|
||||||
saveFailed: 'Failed to save rectifier settings'
|
saveFailed: 'Failed to save rectifier settings'
|
||||||
},
|
},
|
||||||
|
|||||||
@ -4637,6 +4637,14 @@ export default {
|
|||||||
thinkingSignatureHint: '当上游返回 thinking block 签名校验错误时,自动去除签名并重试',
|
thinkingSignatureHint: '当上游返回 thinking block 签名校验错误时,自动去除签名并重试',
|
||||||
thinkingBudget: 'Thinking Budget 整流',
|
thinkingBudget: 'Thinking Budget 整流',
|
||||||
thinkingBudgetHint: '当上游返回 budget_tokens 约束错误(≥1024)时,自动将 budget 设为 32000 并重试',
|
thinkingBudgetHint: '当上游返回 budget_tokens 约束错误(≥1024)时,自动将 budget 设为 32000 并重试',
|
||||||
|
apikeySignature: 'API Key 签名整流',
|
||||||
|
apikeySignatureHint:
|
||||||
|
'当 API Key 账号的上游返回签名相关错误时,自动去除签名并重试(内置规则始终生效)',
|
||||||
|
apikeyPatterns: '自定义匹配关键词',
|
||||||
|
apikeyPatternsHint:
|
||||||
|
'额外的关键词,匹配响应体中的内容(不区分大小写)。内置规则始终生效,此处用于补充额外匹配。',
|
||||||
|
apikeyPatternPlaceholder: '例如:thinking_error 或 签名无效',
|
||||||
|
addPattern: '添加关键词',
|
||||||
saved: '整流器设置保存成功',
|
saved: '整流器设置保存成功',
|
||||||
saveFailed: '保存整流器设置失败'
|
saveFailed: '保存整流器设置失败'
|
||||||
},
|
},
|
||||||
|
|||||||
@ -454,6 +454,72 @@
|
|||||||
</div>
|
</div>
|
||||||
<Toggle v-model="rectifierForm.thinking_budget_enabled" />
|
<Toggle v-model="rectifierForm.thinking_budget_enabled" />
|
||||||
</div>
|
</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>
|
</div>
|
||||||
|
|
||||||
<!-- Save Button -->
|
<!-- Save Button -->
|
||||||
@ -2010,7 +2076,9 @@ const rectifierSaving = ref(false)
|
|||||||
const rectifierForm = reactive({
|
const rectifierForm = reactive({
|
||||||
enabled: true,
|
enabled: true,
|
||||||
thinking_signature_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 状态
|
// Beta Policy 状态
|
||||||
@ -2626,6 +2694,10 @@ async function loadRectifierSettings() {
|
|||||||
try {
|
try {
|
||||||
const settings = await adminAPI.settings.getRectifierSettings()
|
const settings = await adminAPI.settings.getRectifierSettings()
|
||||||
Object.assign(rectifierForm, settings)
|
Object.assign(rectifierForm, settings)
|
||||||
|
// 确保 patterns 是数组(旧数据可能为 null)
|
||||||
|
if (!Array.isArray(rectifierForm.apikey_signature_patterns)) {
|
||||||
|
rectifierForm.apikey_signature_patterns = []
|
||||||
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('Failed to load rectifier settings:', error)
|
console.error('Failed to load rectifier settings:', error)
|
||||||
} finally {
|
} finally {
|
||||||
@ -2639,9 +2711,16 @@ async function saveRectifierSettings() {
|
|||||||
const updated = await adminAPI.settings.updateRectifierSettings({
|
const updated = await adminAPI.settings.updateRectifierSettings({
|
||||||
enabled: rectifierForm.enabled,
|
enabled: rectifierForm.enabled,
|
||||||
thinking_signature_enabled: rectifierForm.thinking_signature_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)
|
Object.assign(rectifierForm, updated)
|
||||||
|
if (!Array.isArray(rectifierForm.apikey_signature_patterns)) {
|
||||||
|
rectifierForm.apikey_signature_patterns = []
|
||||||
|
}
|
||||||
appStore.showSuccess(t('admin.settings.rectifier.saved'))
|
appStore.showSuccess(t('admin.settings.rectifier.saved'))
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
appStore.showError(
|
appStore.showError(
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user