diff --git a/prompting/prompt-profiles.js b/prompting/prompt-profiles.js index a19e2ed..0162ef8 100644 --- a/prompting/prompt-profiles.js +++ b/prompting/prompt-profiles.js @@ -896,6 +896,119 @@ const DEFAULT_TASK_REGEX_STAGES = Object.freeze({ output: false, }); + const DEFAULT_GLOBAL_TASK_REGEX_RULE_SPECS = Object.freeze([ + { + id: "default-contamination-thinking-blocks", + script_name: "默认清理:thinking/analysis/reasoning", + enabled: true, + find_regex: "/<(think|thinking|analysis|reasoning)\\b[^>]*>[\\s\\S]*?<\\/\\1>/gi", + replace_string: "", + trim_strings: "", + source: { + user_input: true, + ai_output: true, + }, + destination: { + prompt: true, + display: false, + }, + min_depth: 0, + max_depth: 9999, + }, + { + id: "default-contamination-choice-blocks", + script_name: "默认清理:choice", + enabled: true, + find_regex: "/(?:]*>[\\s\\S]*?<\\/choice>|]*\\/?>)/gi", + replace_string: "", + trim_strings: "", + source: { + user_input: true, + ai_output: true, + }, + destination: { + prompt: true, + display: false, + }, + min_depth: 0, + max_depth: 9999, + }, + { + id: "default-contamination-updatevariable-tags", + script_name: "默认清理:UpdateVariable", + enabled: true, + find_regex: + "/(?:]*>[\\s\\S]*?<\\/updatevariable>|]*\\/?>)/gi", + replace_string: "", + trim_strings: "", + source: { + user_input: true, + ai_output: true, + }, + destination: { + prompt: true, + display: false, + }, + min_depth: 0, + max_depth: 9999, + }, + { + id: "default-contamination-status-current-variable-tags", + script_name: "默认清理:status_current_variable", + enabled: true, + find_regex: + "/(?:]*>[\\s\\S]*?<\\/status_current_variable>|]*\\/?>)/gi", + replace_string: "", + trim_strings: "", + source: { + user_input: true, + ai_output: true, + }, + destination: { + prompt: true, + display: false, + }, + min_depth: 0, + max_depth: 9999, + }, + { + id: "default-contamination-status-placeholder-tags", + script_name: "默认清理:StatusPlaceHolderImpl", + enabled: true, + find_regex: "/]*\\/?>/gi", + replace_string: "", + trim_strings: "", + source: { + user_input: true, + ai_output: true, + }, + destination: { + prompt: true, + display: false, + }, + min_depth: 0, + max_depth: 9999, + }, + ]); + + function cloneDefaultGlobalTaskRegexRules() { + return DEFAULT_GLOBAL_TASK_REGEX_RULE_SPECS.map((rule, index) => + normalizeRegexLocalRule( + { + ...rule, + source: { + ...(rule.source || {}), + }, + destination: { + ...(rule.destination || {}), + }, + }, + "global", + index, + ), + ); + } + function normalizeRegexStageKey(stageKey = "") { const normalized = String(stageKey || "").trim(); return TASK_REGEX_STAGE_ALIAS_MAP[normalized] || normalized; @@ -939,7 +1052,7 @@ export function createDefaultGlobalTaskRegex() { character: true, }, stages: normalizeTaskRegexStages(DEFAULT_TASK_REGEX_STAGES), - localRules: [], + localRules: cloneDefaultGlobalTaskRegexRules(), }; } @@ -978,6 +1091,11 @@ export function normalizeGlobalTaskRegex(config = {}, taskType = "global") { const defaults = createDefaultGlobalTaskRegex(); const source = config && typeof config === "object" && !Array.isArray(config) ? config : {}; + const normalizedTaskType = String(taskType || "").trim().toLowerCase(); + const defaultLocalRules = normalizedTaskType === "global" ? defaults.localRules : []; + const rawLocalRules = Array.isArray(source.localRules) + ? source.localRules + : defaultLocalRules; return { enabled: source.enabled !== false, @@ -990,7 +1108,7 @@ export function normalizeGlobalTaskRegex(config = {}, taskType = "global") { ...normalizeTaskRegexStages(defaults.stages), ...normalizeTaskRegexStages(source.stages || {}), }, - localRules: dedupeRegexRules(source.localRules, taskType), + localRules: dedupeRegexRules(rawLocalRules, taskType), }; } diff --git a/tests/default-settings.mjs b/tests/default-settings.mjs index 4feaec5..f56a161 100644 --- a/tests/default-settings.mjs +++ b/tests/default-settings.mjs @@ -70,6 +70,17 @@ assert.equal(defaultSettings.taskProfilesVersion, 3); assert.ok(defaultSettings.taskProfiles); assert.ok(defaultSettings.taskProfiles.extract); assert.ok(defaultSettings.taskProfiles.recall); +assert.ok(defaultSettings.globalTaskRegex); +assert.deepEqual( + defaultSettings.globalTaskRegex.localRules.map((rule) => rule.id), + [ + "default-contamination-thinking-blocks", + "default-contamination-choice-blocks", + "default-contamination-updatevariable-tags", + "default-contamination-status-current-variable-tags", + "default-contamination-status-placeholder-tags", + ], +); const migratedSettings = mergePersistedSettings({ maintenanceAutoMinNewNodes: 7, diff --git a/tests/prompt-builder-defaults.mjs b/tests/prompt-builder-defaults.mjs index 4e6b923..380ce7c 100644 --- a/tests/prompt-builder-defaults.mjs +++ b/tests/prompt-builder-defaults.mjs @@ -46,7 +46,10 @@ installResolveHooks([ ]); const { buildTaskLlmPayload, buildTaskPrompt } = await import("../prompting/prompt-builder.js"); -const { createDefaultTaskProfiles } = await import("../prompting/prompt-profiles.js"); +const { + createDefaultGlobalTaskRegex, + createDefaultTaskProfiles, +} = await import("../prompting/prompt-profiles.js"); const { initializeHostAdapter } = await import("../host/adapter/index.js"); const settings = { @@ -145,6 +148,28 @@ assert.match(String(recallFormatBlock?.content || ""), /selected_keys/); assert.match(String(recallRulesBlock?.content || ""), /剧情时间/); assert.match(String(recallRulesBlock?.content || ""), /评分召回/); +const globalRegexPromptBuild = await buildTaskPrompt( + { + taskProfilesVersion: 3, + taskProfiles: createDefaultTaskProfiles(), + globalTaskRegex: createDefaultGlobalTaskRegex(), + }, + "recall", + { + taskName: "recall", + recentMessages: + "最近消息 隐藏思维 1. 隐藏选项", + userMessage: + "用户输入 secret hp=3", + candidateNodes: + "候选节点 隐藏分析", + }, +); +assert.doesNotMatch( + JSON.stringify(globalRegexPromptBuild), + / rule.script_name), - ["隐藏规则"], + [ + "默认清理:thinking/analysis/reasoning", + "默认清理:choice", + "默认清理:UpdateVariable", + "默认清理:status_current_variable", + "默认清理:StatusPlaceHolderImpl", + "隐藏规则", + ], ); assert.deepEqual( migratedLegacyRegex.settings.taskProfiles.extract.profiles.find( diff --git a/tests/task-regex.mjs b/tests/task-regex.mjs index fb662b9..b455316 100644 --- a/tests/task-regex.mjs +++ b/tests/task-regex.mjs @@ -175,6 +175,7 @@ try { "../prompting/task-regex.js" ); const { + createDefaultGlobalTaskRegex, createDefaultTaskProfiles, isTaskRegexStageEnabled, normalizeTaskProfile, @@ -933,6 +934,93 @@ try { ["prompt-output"], ); + const defaultGlobalRegex = createDefaultGlobalTaskRegex(); + assert.deepEqual( + defaultGlobalRegex.localRules.map((rule) => rule.id), + [ + "default-contamination-thinking-blocks", + "default-contamination-choice-blocks", + "default-contamination-updatevariable-tags", + "default-contamination-status-current-variable-tags", + "default-contamination-status-placeholder-tags", + ], + ); + + const globalDefaultDebug = { entries: [] }; + const globalDefaultResult = applyTaskRegex( + { + taskProfiles: createDefaultTaskProfiles(), + globalTaskRegex: createDefaultGlobalTaskRegex(), + }, + "extract", + "input.recentMessages", + [ + "前缀", + "内部思维", + "1. 选项", + "hp=1", + "hp=1", + "", + "尾巴", + ].join("\n"), + globalDefaultDebug, + "system", + ); + assert.match(globalDefaultResult, /前缀/); + assert.match(globalDefaultResult, /尾巴/); + assert.doesNotMatch( + globalDefaultResult, + / item.id), + [ + "default-contamination-thinking-blocks", + "default-contamination-choice-blocks", + "default-contamination-updatevariable-tags", + "default-contamination-status-current-variable-tags", + "default-contamination-status-placeholder-tags", + ], + ); + assert.equal(globalDefaultDebug.entries[0].sourceCount.local, 5); + + const explicitEmptyGlobalDebug = { entries: [] }; + const explicitEmptyGlobalResult = applyTaskRegex( + { + taskProfiles: createDefaultTaskProfiles(), + globalTaskRegex: { + enabled: true, + inheritStRegex: false, + sources: { + global: false, + preset: false, + character: false, + }, + stages: { + "input.userMessage": true, + "input.recentMessages": true, + "input.candidateText": true, + "input.finalPrompt": false, + "output.rawResponse": false, + "output.beforeParse": false, + output: false, + }, + localRules: [], + }, + }, + "extract", + "input.recentMessages", + "保留保留", + explicitEmptyGlobalDebug, + "system", + ); + assert.equal( + explicitEmptyGlobalResult, + "保留保留", + ); + assert.deepEqual(explicitEmptyGlobalDebug.entries[0].appliedRules, []); + assert.equal(explicitEmptyGlobalDebug.entries[0].sourceCount.local, 0); + console.log("task-regex tests passed"); } finally { if (originalSillyTavern === undefined) {