fix(ai): ensure builtin masking rules before preprocessing

This commit is contained in:
digua committed 2026-06-04 14:37:12 +08:00
1 parent cd9f297124
commit f4580c55f1
3 files changed
+105 -24

No files matched your search

+8 -24
View File
@@ -17,6 +17,7 @@ import { useSkillStore } from '@/stores/skill'
import { useLLMStore } from '@/stores/llm'
import type { TokenUsage, AgentRuntimeStatus, SerializedErrorInfo } from '@electron/shared/types'
import { useAgentStreamService } from '@/services/ai-stream/service'
import { buildSerializablePreprocessConfig, shouldEnsureDesensitizeRulesBeforeSerialize } from './aiPreprocessConfig'
// 工具调用记录
export interface ToolCallRecord {
@@ -811,7 +812,7 @@ export const useAIChatStore = defineStore('aiChatRuntime', () => {
? { platformId: state.ownerInfo.platformId, displayName: state.ownerInfo.displayName }
: undefined,
mentionedMembers: currentMentionedMembers.length > 0 ? currentMentionedMembers : undefined,
preprocessConfig: buildSerializablePreprocessConfig(),
preprocessConfig: await buildReadySerializablePreprocessConfig(),
searchContextBefore: aiGlobalSettings.value.searchContextBefore,
searchContextAfter: aiGlobalSettings.value.searchContextAfter,
}
@@ -1096,30 +1097,13 @@ export const useAIChatStore = defineStore('aiChatRuntime', () => {
}
}
function buildSerializablePreprocessConfig() {
async function buildReadySerializablePreprocessConfig() {
const preprocessConfig = settingsStore.aiPreprocessConfig
const hasPreprocess =
preprocessConfig.dataCleaning ||
preprocessConfig.mergeConsecutive ||
preprocessConfig.blacklistKeywords.length > 0 ||
preprocessConfig.denoise ||
preprocessConfig.desensitize ||
preprocessConfig.anonymizeNames
if (!hasPreprocess) return undefined
return {
dataCleaning: preprocessConfig.dataCleaning,
mergeConsecutive: preprocessConfig.mergeConsecutive,
mergeWindowSeconds: preprocessConfig.mergeWindowSeconds,
blacklistKeywords: [...preprocessConfig.blacklistKeywords],
denoise: preprocessConfig.denoise,
desensitize: preprocessConfig.desensitize,
desensitizeRules: preprocessConfig.desensitizeRules.map((rule) => ({
...rule,
locales: [...rule.locales],
})),
anonymizeNames: preprocessConfig.anonymizeNames,
if (shouldEnsureDesensitizeRulesBeforeSerialize(preprocessConfig)) {
await settingsStore.ensureDesensitizeRules()
}
return buildSerializablePreprocessConfig(settingsStore.aiPreprocessConfig)
}
function normalizeMentionLookupText(value: string): string {
@@ -1360,7 +1344,7 @@ export const useAIChatStore = defineStore('aiChatRuntime', () => {
? { platformId: state.ownerInfo.platformId, displayName: state.ownerInfo.displayName }
: undefined,
mentionedMembers: currentMentionedMembers.length > 0 ? currentMentionedMembers : undefined,
preprocessConfig: buildSerializablePreprocessConfig(),
preprocessConfig: await buildReadySerializablePreprocessConfig(),
searchContextBefore: aiGlobalSettings.value.searchContextBefore,
searchContextAfter: aiGlobalSettings.value.searchContextAfter,
}
+65
View File
@@ -0,0 +1,65 @@
import { describe, it } from 'node:test'
import assert from 'node:assert/strict'
import { shouldEnsureDesensitizeRulesBeforeSerialize } from './aiPreprocessConfig'
describe('AI preprocess config serialization guards', () => {
it('requires built-in rules before serializing an enabled empty desensitize config', () => {
assert.equal(
shouldEnsureDesensitizeRulesBeforeSerialize({
desensitize: true,
desensitizeRules: [],
}),
true
)
})
it('requires built-in rules before serializing enabled custom-only desensitize rules', () => {
assert.equal(
shouldEnsureDesensitizeRulesBeforeSerialize({
desensitize: true,
desensitizeRules: [
{
id: 'custom_staff_id',
label: 'Staff ID',
pattern: 'EMP-\\d+',
replacement: '[Staff ID]',
enabled: true,
builtin: false,
locales: [],
},
],
}),
true
)
})
it('does not require built-in rules when enabled rules already include a built-in rule', () => {
assert.equal(
shouldEnsureDesensitizeRulesBeforeSerialize({
desensitize: true,
desensitizeRules: [
{
id: 'email',
label: 'desensitize.rules.email',
pattern: '[^@\\s]+@[^@\\s]+',
replacement: '[Email]',
enabled: true,
builtin: true,
locales: [],
},
],
}),
false
)
})
it('does not require built-in rules when masking is disabled', () => {
assert.equal(
shouldEnsureDesensitizeRulesBeforeSerialize({
desensitize: false,
desensitizeRules: [],
}),
false
)
})
})
+32
View File
@@ -0,0 +1,32 @@
import type { PreprocessConfig } from '@electron/preload/index'
export function shouldEnsureDesensitizeRulesBeforeSerialize(
config: Pick<PreprocessConfig, 'desensitize' | 'desensitizeRules'>
): boolean {
return config.desensitize && !config.desensitizeRules.some((rule) => rule.builtin)
}
export function buildSerializablePreprocessConfig(config: PreprocessConfig) {
const hasPreprocess =
config.dataCleaning ||
config.mergeConsecutive ||
config.blacklistKeywords.length > 0 ||
config.denoise ||
config.desensitize ||
config.anonymizeNames
if (!hasPreprocess) return undefined
return {
dataCleaning: config.dataCleaning,
mergeConsecutive: config.mergeConsecutive,
mergeWindowSeconds: config.mergeWindowSeconds,
blacklistKeywords: [...config.blacklistKeywords],
denoise: config.denoise,
desensitize: config.desensitize,
desensitizeRules: config.desensitizeRules.map((rule) => ({
...rule,
locales: [...rule.locales],
})),
anonymizeNames: config.anonymizeNames,
}
}