From 429b5eeec441537d313a48c6b73956c85d8a9687 Mon Sep 17 00:00:00 2001
From: Youzini-afk <13153778771cx@gmail.com>
Date: Tue, 7 Apr 2026 00:32:14 +0800
Subject: [PATCH] Fix prompt-stage beautification regex stripping
---
panel.js | 8 ++++-
task-regex.js | 25 +++++++++++----
tests/task-regex.mjs | 75 ++++++++++++++++++++++++++++++++++++++++++--
3 files changed, 99 insertions(+), 9 deletions(-)
diff --git a/panel.js b/panel.js
index 4a68d58..bfa418c 100644
--- a/panel.js
+++ b/panel.js
@@ -3890,8 +3890,14 @@ function _renderRegexReuseRuleList(rules = [], emptyText = "无") {
const flags = [
rule.promptOnly ? "promptOnly" : "",
rule.markdownOnly ? "markdownOnly" : "",
+ rule.promptReplaceAsEmpty ? "请求阶段按空字符串替换" : "",
rule.reason ? `原因: ${rule.reason}` : "",
].filter(Boolean);
+ const replaceText = rule.promptReplaceAsEmpty
+ ? `${rule.replaceString ? `${_escHtml(rule.replaceString)} -> ` : ""}(prompt 时为空)`
+ : rule.replaceString
+ ? `${_escHtml(rule.replaceString)}`
+ : "";
return `
${_escHtml(rule.name || rule.id || "未命名规则")}
@@ -3899,7 +3905,7 @@ function _renderRegexReuseRuleList(rules = [], emptyText = "无") {
${_escHtml(rule.findRegex || "(空 findRegex)")}
- ${rule.replaceString ? ` -> ${_escHtml(rule.replaceString)}` : ""}
+ ${replaceText ? ` -> ${replaceText}` : ""}
${flags.length ? `
${_escHtml(flags.join(" · "))}` : ""}
`;
diff --git a/task-regex.js b/task-regex.js
index 361c7ae..fcbe057 100644
--- a/task-regex.js
+++ b/task-regex.js
@@ -154,7 +154,9 @@ function normalizeRule(raw = {}, fallbackSource = "local", index = 0) {
destinationFlags: {
prompt: destination
? Boolean(destination.prompt)
- : raw.markdownOnly !== true,
+ : isTavernRule && raw.markdownOnly === true
+ ? true
+ : raw.markdownOnly !== true,
display: destination
? Boolean(destination.display)
: Boolean(raw.markdownOnly),
@@ -376,11 +378,18 @@ function getPlacementLabels(placement = []) {
function summarizeRule(rule, reason = "") {
const normalized = rule && typeof rule === "object" ? rule : {};
+ const promptReplaceAsEmpty =
+ Boolean(normalized.markdownOnly) ||
+ isBeautificationReplace(normalized.replaceString);
return {
id: String(normalized.id || ""),
name: String(normalized.scriptName || normalized.id || ""),
findRegex: String(normalized.findRegex || ""),
replaceString: String(normalized.replaceString || ""),
+ effectivePromptReplaceString: promptReplaceAsEmpty
+ ? ""
+ : String(normalized.replaceString || ""),
+ promptReplaceAsEmpty,
sourceType: String(normalized.sourceType || ""),
promptOnly: Boolean(normalized.promptOnly),
markdownOnly: Boolean(normalized.markdownOnly),
@@ -629,15 +638,16 @@ function shouldApplyRuleForTaskContext(rule, stage = "") {
return true;
}
- if (rule.markdownOnly) {
- return false;
- }
-
const normalizedStage = String(stage || "").trim();
+ const isPromptStage = PROMPT_STAGES.has(normalizedStage);
const isFinalPromptStage =
normalizedStage === "finalPrompt" || normalizedStage === "input.finalPrompt";
const isOutputStage = OUTPUT_STAGES.has(normalizedStage);
+ if (rule.markdownOnly) {
+ return isPromptStage;
+ }
+
if (isFinalPromptStage) {
return rule.promptOnly === true;
}
@@ -683,7 +693,10 @@ function applyOneRule(input, rule, stage = "") {
if (!regex) return { output: input, changed: false, error: "invalid_regex" };
let replacement = rule.replaceString || "";
- if (PROMPT_STAGES.has(stage) && isBeautificationReplace(replacement)) {
+ if (
+ PROMPT_STAGES.has(stage) &&
+ (rule.markdownOnly || isBeautificationReplace(replacement))
+ ) {
replacement = "";
}
diff --git a/tests/task-regex.mjs b/tests/task-regex.mjs
index 8bad23c..2a1f010 100644
--- a/tests/task-regex.mjs
+++ b/tests/task-regex.mjs
@@ -443,7 +443,7 @@ try {
placement: [PLACEMENT.USER_INPUT],
promptOnly: true,
}),
- createTavernRule("markdown-only", "/Alpha/g", "M", {
+ createTavernRule("markdown-only", "/Alpha/g", "M", {
placement: [PLACEMENT.USER_INPUT],
markdownOnly: true,
}),
@@ -475,7 +475,7 @@ try {
{ entries: [] },
"user",
),
- "Alpha",
+ "",
);
assert.equal(
applyTaskRegex(
@@ -499,6 +499,77 @@ try {
),
"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", "Decor", {
+ 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", "M", {
+ 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(
applyTaskRegex(
tavernSemanticsSettings,