Fix prompt-stage beautification regex stripping

This commit is contained in:
Youzini-afk
2026-04-07 00:32:14 +08:00
parent 71d70c205f
commit 429b5eeec4
3 changed files with 99 additions and 9 deletions

View File

@@ -3890,8 +3890,14 @@ function _renderRegexReuseRuleList(rules = [], emptyText = "无") {
const flags = [ const flags = [
rule.promptOnly ? "promptOnly" : "", rule.promptOnly ? "promptOnly" : "",
rule.markdownOnly ? "markdownOnly" : "", rule.markdownOnly ? "markdownOnly" : "",
rule.promptReplaceAsEmpty ? "请求阶段按空字符串替换" : "",
rule.reason ? `原因: ${rule.reason}` : "", rule.reason ? `原因: ${rule.reason}` : "",
].filter(Boolean); ].filter(Boolean);
const replaceText = rule.promptReplaceAsEmpty
? `${rule.replaceString ? `<code>${_escHtml(rule.replaceString)}</code> -> ` : ""}<code>(prompt 时为空)</code>`
: rule.replaceString
? `<code>${_escHtml(rule.replaceString)}</code>`
: "";
return ` return `
<div class="bme-debug-row"> <div class="bme-debug-row">
<span class="bme-debug-key">${_escHtml(rule.name || rule.id || "未命名规则")}</span> <span class="bme-debug-key">${_escHtml(rule.name || rule.id || "未命名规则")}</span>
@@ -3899,7 +3905,7 @@ function _renderRegexReuseRuleList(rules = [], emptyText = "无") {
</div> </div>
<div class="bme-task-note"> <div class="bme-task-note">
<code>${_escHtml(rule.findRegex || "(空 findRegex)")}</code> <code>${_escHtml(rule.findRegex || "(空 findRegex)")}</code>
${rule.replaceString ? ` -> <code>${_escHtml(rule.replaceString)}</code>` : ""} ${replaceText ? ` -> ${replaceText}` : ""}
${flags.length ? `<br>${_escHtml(flags.join(" · "))}` : ""} ${flags.length ? `<br>${_escHtml(flags.join(" · "))}` : ""}
</div> </div>
`; `;

View File

@@ -154,7 +154,9 @@ function normalizeRule(raw = {}, fallbackSource = "local", index = 0) {
destinationFlags: { destinationFlags: {
prompt: destination prompt: destination
? Boolean(destination.prompt) ? Boolean(destination.prompt)
: raw.markdownOnly !== true, : isTavernRule && raw.markdownOnly === true
? true
: raw.markdownOnly !== true,
display: destination display: destination
? Boolean(destination.display) ? Boolean(destination.display)
: Boolean(raw.markdownOnly), : Boolean(raw.markdownOnly),
@@ -376,11 +378,18 @@ function getPlacementLabels(placement = []) {
function summarizeRule(rule, reason = "") { function summarizeRule(rule, reason = "") {
const normalized = rule && typeof rule === "object" ? rule : {}; const normalized = rule && typeof rule === "object" ? rule : {};
const promptReplaceAsEmpty =
Boolean(normalized.markdownOnly) ||
isBeautificationReplace(normalized.replaceString);
return { return {
id: String(normalized.id || ""), id: String(normalized.id || ""),
name: String(normalized.scriptName || normalized.id || ""), name: String(normalized.scriptName || normalized.id || ""),
findRegex: String(normalized.findRegex || ""), findRegex: String(normalized.findRegex || ""),
replaceString: String(normalized.replaceString || ""), replaceString: String(normalized.replaceString || ""),
effectivePromptReplaceString: promptReplaceAsEmpty
? ""
: String(normalized.replaceString || ""),
promptReplaceAsEmpty,
sourceType: String(normalized.sourceType || ""), sourceType: String(normalized.sourceType || ""),
promptOnly: Boolean(normalized.promptOnly), promptOnly: Boolean(normalized.promptOnly),
markdownOnly: Boolean(normalized.markdownOnly), markdownOnly: Boolean(normalized.markdownOnly),
@@ -629,15 +638,16 @@ function shouldApplyRuleForTaskContext(rule, stage = "") {
return true; return true;
} }
if (rule.markdownOnly) {
return false;
}
const normalizedStage = String(stage || "").trim(); const normalizedStage = String(stage || "").trim();
const isPromptStage = PROMPT_STAGES.has(normalizedStage);
const isFinalPromptStage = const isFinalPromptStage =
normalizedStage === "finalPrompt" || normalizedStage === "input.finalPrompt"; normalizedStage === "finalPrompt" || normalizedStage === "input.finalPrompt";
const isOutputStage = OUTPUT_STAGES.has(normalizedStage); const isOutputStage = OUTPUT_STAGES.has(normalizedStage);
if (rule.markdownOnly) {
return isPromptStage;
}
if (isFinalPromptStage) { if (isFinalPromptStage) {
return rule.promptOnly === true; return rule.promptOnly === true;
} }
@@ -683,7 +693,10 @@ function applyOneRule(input, rule, stage = "") {
if (!regex) return { output: input, changed: false, error: "invalid_regex" }; if (!regex) return { output: input, changed: false, error: "invalid_regex" };
let replacement = rule.replaceString || ""; let replacement = rule.replaceString || "";
if (PROMPT_STAGES.has(stage) && isBeautificationReplace(replacement)) { if (
PROMPT_STAGES.has(stage) &&
(rule.markdownOnly || isBeautificationReplace(replacement))
) {
replacement = ""; replacement = "";
} }

View File

@@ -443,7 +443,7 @@ try {
placement: [PLACEMENT.USER_INPUT], placement: [PLACEMENT.USER_INPUT],
promptOnly: true, promptOnly: true,
}), }),
createTavernRule("markdown-only", "/Alpha/g", "M", { createTavernRule("markdown-only", "/Alpha/g", "<b>M</b>", {
placement: [PLACEMENT.USER_INPUT], placement: [PLACEMENT.USER_INPUT],
markdownOnly: true, markdownOnly: true,
}), }),
@@ -475,7 +475,7 @@ try {
{ entries: [] }, { entries: [] },
"user", "user",
), ),
"Alpha", "",
); );
assert.equal( assert.equal(
applyTaskRegex( applyTaskRegex(
@@ -499,6 +499,77 @@ try {
), ),
"AI Lore", "AI Lore",
); );
const markdownInspect = inspectTaskRegexReuse(tavernSemanticsSettings, "extract");
const markdownRule = markdownInspect.activeRules.find(
(rule) => rule.id === "markdown-only",
);
assert.equal(markdownRule?.promptReplaceAsEmpty, true);
assert.equal(markdownRule?.effectivePromptReplaceString, "");
const markdownOnlyFinalPromptSettings = buildSettings({
sources: {
global: true,
preset: false,
character: false,
},
});
setTestContext({
extensionSettings: {
regex: [
createTavernRule("markdown-final-strip", "/Decor/g", "<span>Decor</span>", {
placement: [PLACEMENT.USER_INPUT],
markdownOnly: true,
}),
],
preset_allowed_regex: {},
character_allowed_regex: [],
},
});
initializeHostAdapter({});
const markdownFinalDebug = { entries: [] };
assert.equal(
applyTaskRegex(
markdownOnlyFinalPromptSettings,
"extract",
"input.finalPrompt",
"Decor",
markdownFinalDebug,
"user",
),
"",
);
assert.deepEqual(
markdownFinalDebug.entries[0].appliedRules.map((item) => item.id),
["markdown-final-strip"],
);
setTestContext({
extensionSettings: {
regex: [
createTavernRule("user-prompt-only", "/Alpha/g", "A", {
placement: [PLACEMENT.USER_INPUT],
promptOnly: true,
}),
createTavernRule("markdown-only", "/Alpha/g", "<b>M</b>", {
placement: [PLACEMENT.USER_INPUT],
markdownOnly: true,
}),
createTavernRule("output-only", "/Answer/g", "AI", {
placement: [PLACEMENT.AI_OUTPUT],
}),
createTavernRule("world-info-only", "/Lore/g", "SYS", {
placement: [PLACEMENT.WORLD_INFO],
}),
createTavernRule("recent-user", "/User/g", "U", {
placement: [PLACEMENT.USER_INPUT],
}),
createTavernRule("recent-ai", "/Reply/g", "R", {
placement: [PLACEMENT.AI_OUTPUT],
}),
],
preset_allowed_regex: {},
character_allowed_regex: [],
},
});
initializeHostAdapter({});
assert.equal( assert.equal(
applyTaskRegex( applyTaskRegex(
tavernSemanticsSettings, tavernSemanticsSettings,