mirror of
https://github.com/Youzini-afk/ST-Bionic-Memory-Ecology.git
synced 2026-05-15 22:30:38 +08:00
Fix MVU stripping order for world info prompts
This commit is contained in:
@@ -11,11 +11,24 @@ const MVU_STATUS_CURRENT_VARIABLE_REPLACE_REGEX =
|
|||||||
/\n?<status_current_variables?>[\s\S]*?<\/status_current_variables?>/gi;
|
/\n?<status_current_variables?>[\s\S]*?<\/status_current_variables?>/gi;
|
||||||
const MVU_STATUS_CURRENT_VARIABLE_DETECT_REGEX =
|
const MVU_STATUS_CURRENT_VARIABLE_DETECT_REGEX =
|
||||||
/<status_current_variables?>[\s\S]*?<\/status_current_variables?>/i;
|
/<status_current_variables?>[\s\S]*?<\/status_current_variables?>/i;
|
||||||
|
const MVU_MESSAGE_VARIABLE_MACRO_REGEX =
|
||||||
|
/\{\{\s*get_message_variable::(?:stat_data|display_data|delta_data)(?:\.[^}]+)?\s*}}/gi;
|
||||||
|
const MVU_GETVAR_REFERENCE_REGEX =
|
||||||
|
/getvar\(\s*["'](?:stat_data|display_data|delta_data)["']\s*\)/gi;
|
||||||
|
const MVU_STATEFUL_TEMPLATE_TAG_REGEX =
|
||||||
|
/<%[-=]?[\s\S]*?(?:SafeGetValue|getvar\(\s*["'](?:stat_data|display_data|delta_data)["']\s*\)|\b(?:stat_data|display_data|delta_data)\b)[\s\S]*?%>/gi;
|
||||||
|
const EJS_TEMPLATE_TAG_REGEX = /<%[-=]?[\s\S]*?%>/gi;
|
||||||
const MVU_VARIABLE_OUTPUT_ENTRY_REGEX = /变量输出格式:\s*[\s\S]*?<UpdateVariable>/i;
|
const MVU_VARIABLE_OUTPUT_ENTRY_REGEX = /变量输出格式:\s*[\s\S]*?<UpdateVariable>/i;
|
||||||
const MVU_VARIABLE_RULES_ENTRY_REGEX =
|
const MVU_VARIABLE_RULES_ENTRY_REGEX =
|
||||||
/变量更新规则:\s*[\s\S]*?(?:type:\s*|check:\s*|当前时间:|近期事务:)/i;
|
/变量更新规则:\s*[\s\S]*?(?:type:\s*|check:\s*|当前时间:|近期事务:)/i;
|
||||||
const MVU_FORMAT_EMPHASIS_ENTRY_REGEX =
|
const MVU_FORMAT_EMPHASIS_ENTRY_REGEX =
|
||||||
/(?:变量输出格式强调|格式强调[::]?-?变量更新规则|格式强调[::]?-?剧情演绎|The following must be inserted to the end of (?:each )?reply,? and cannot be omitted)[\s\S]*?format:\s*\|-?/i;
|
/(?:变量输出格式强调|格式强调[::]?-?变量更新规则|格式强调[::]?-?剧情演绎|The following must be inserted to the end of (?:each )?reply,? and cannot be omitted)[\s\S]*?format:\s*\|-?/i;
|
||||||
|
const MVU_STATE_OBJECT_FIELD_REGEX =
|
||||||
|
/["']?(?:stat_data|display_data|delta_data)["']?\s*:/i;
|
||||||
|
const MVU_STATE_PATH_REFERENCE_REGEX =
|
||||||
|
/\b(?:stat_data|display_data|delta_data)(?:\.[\w$\u4e00-\u9fff\[\]"'-]+){1,}/i;
|
||||||
|
const MVU_STATE_HELPER_REFERENCE_REGEX =
|
||||||
|
/\b(?:SafeGetValue\([^)]*(?:stat_data|display_data|delta_data)[^)]*\)|message_data\[\d+\]\.data\.(?:stat_data|display_data|delta_data))\b/i;
|
||||||
|
|
||||||
function uniq(values = []) {
|
function uniq(values = []) {
|
||||||
return [...new Set((Array.isArray(values) ? values : []).filter(Boolean))];
|
return [...new Set((Array.isArray(values) ? values : []).filter(Boolean))];
|
||||||
@@ -45,6 +58,14 @@ function countRegexMatches(text = "", regex) {
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function matchesRegex(text = "", regex) {
|
||||||
|
if (!text || !(regex instanceof RegExp)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new RegExp(regex.source, regex.flags).test(text);
|
||||||
|
}
|
||||||
|
|
||||||
function stripMvuPromptArtifactsDetailed(content = "") {
|
function stripMvuPromptArtifactsDetailed(content = "") {
|
||||||
const input = normalizeText(content);
|
const input = normalizeText(content);
|
||||||
if (!input) {
|
if (!input) {
|
||||||
@@ -55,15 +76,28 @@ function stripMvuPromptArtifactsDetailed(content = "") {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const statefulTemplateTagCount = countRegexMatches(
|
||||||
|
input,
|
||||||
|
MVU_STATEFUL_TEMPLATE_TAG_REGEX,
|
||||||
|
);
|
||||||
const artifactRemovedCount =
|
const artifactRemovedCount =
|
||||||
countRegexMatches(input, MVU_UPDATE_BLOCK_REGEX) +
|
countRegexMatches(input, MVU_UPDATE_BLOCK_REGEX) +
|
||||||
countRegexMatches(input, MVU_STATUS_PLACEHOLDER_REGEX) +
|
countRegexMatches(input, MVU_STATUS_PLACEHOLDER_REGEX) +
|
||||||
countRegexMatches(input, MVU_STATUS_CURRENT_VARIABLE_REPLACE_REGEX);
|
countRegexMatches(input, MVU_STATUS_CURRENT_VARIABLE_REPLACE_REGEX) +
|
||||||
|
countRegexMatches(input, MVU_MESSAGE_VARIABLE_MACRO_REGEX) +
|
||||||
|
countRegexMatches(input, MVU_GETVAR_REFERENCE_REGEX) +
|
||||||
|
statefulTemplateTagCount;
|
||||||
|
|
||||||
const stripped = input
|
let stripped = input
|
||||||
.replace(MVU_UPDATE_BLOCK_REGEX, "")
|
.replace(MVU_UPDATE_BLOCK_REGEX, "")
|
||||||
.replace(MVU_STATUS_PLACEHOLDER_REGEX, "")
|
.replace(MVU_STATUS_PLACEHOLDER_REGEX, "")
|
||||||
.replace(MVU_STATUS_CURRENT_VARIABLE_REPLACE_REGEX, "");
|
.replace(MVU_STATUS_CURRENT_VARIABLE_REPLACE_REGEX, "")
|
||||||
|
.replace(MVU_MESSAGE_VARIABLE_MACRO_REGEX, "")
|
||||||
|
.replace(MVU_GETVAR_REFERENCE_REGEX, "")
|
||||||
|
.replace(MVU_STATEFUL_TEMPLATE_TAG_REGEX, "");
|
||||||
|
if (statefulTemplateTagCount > 0) {
|
||||||
|
stripped = stripped.replace(EJS_TEMPLATE_TAG_REGEX, "");
|
||||||
|
}
|
||||||
|
|
||||||
const normalized = collapseWhitespace(stripped);
|
const normalized = collapseWhitespace(stripped);
|
||||||
return {
|
return {
|
||||||
@@ -125,12 +159,27 @@ export function isLikelyMvuWorldInfoContent(content = "") {
|
|||||||
if (!normalized) {
|
if (!normalized) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
const stateKeyMentionCount =
|
||||||
|
normalized.match(/\b(?:stat_data|display_data|delta_data)\b/gi)?.length || 0;
|
||||||
|
|
||||||
|
const stateSignals = [
|
||||||
|
MVU_MESSAGE_VARIABLE_MACRO_REGEX,
|
||||||
|
MVU_GETVAR_REFERENCE_REGEX,
|
||||||
|
MVU_STATE_OBJECT_FIELD_REGEX,
|
||||||
|
MVU_STATE_PATH_REFERENCE_REGEX,
|
||||||
|
MVU_STATE_HELPER_REFERENCE_REGEX,
|
||||||
|
].reduce(
|
||||||
|
(count, pattern) => count + (matchesRegex(normalized, pattern) ? 1 : 0),
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
MVU_STATUS_CURRENT_VARIABLE_DETECT_REGEX.test(normalized) ||
|
matchesRegex(normalized, MVU_STATUS_CURRENT_VARIABLE_DETECT_REGEX) ||
|
||||||
MVU_VARIABLE_OUTPUT_ENTRY_REGEX.test(normalized) ||
|
matchesRegex(normalized, MVU_VARIABLE_OUTPUT_ENTRY_REGEX) ||
|
||||||
MVU_VARIABLE_RULES_ENTRY_REGEX.test(normalized) ||
|
matchesRegex(normalized, MVU_VARIABLE_RULES_ENTRY_REGEX) ||
|
||||||
MVU_FORMAT_EMPHASIS_ENTRY_REGEX.test(normalized)
|
matchesRegex(normalized, MVU_FORMAT_EMPHASIS_ENTRY_REGEX) ||
|
||||||
|
stateSignals >= 2 ||
|
||||||
|
(stateSignals >= 1 && stateKeyMentionCount >= 2)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ const WORLD_INFO_VARIABLE_KEYS = [
|
|||||||
const INPUT_CONTEXT_MVU_FIELDS = [
|
const INPUT_CONTEXT_MVU_FIELDS = [
|
||||||
"userMessage",
|
"userMessage",
|
||||||
"recentMessages",
|
"recentMessages",
|
||||||
|
"chatMessages",
|
||||||
"dialogueText",
|
"dialogueText",
|
||||||
"candidateText",
|
"candidateText",
|
||||||
"candidateNodes",
|
"candidateNodes",
|
||||||
@@ -34,6 +35,7 @@ const INPUT_CONTEXT_MVU_FIELDS = [
|
|||||||
const INPUT_REGEX_STAGE_BY_FIELD = {
|
const INPUT_REGEX_STAGE_BY_FIELD = {
|
||||||
userMessage: "input.userMessage",
|
userMessage: "input.userMessage",
|
||||||
recentMessages: "input.recentMessages",
|
recentMessages: "input.recentMessages",
|
||||||
|
chatMessages: "input.recentMessages",
|
||||||
dialogueText: "input.recentMessages",
|
dialogueText: "input.recentMessages",
|
||||||
candidateText: "input.candidateText",
|
candidateText: "input.candidateText",
|
||||||
candidateNodes: "input.candidateText",
|
candidateNodes: "input.candidateText",
|
||||||
@@ -263,13 +265,23 @@ function sanitizeTaskPromptText(
|
|||||||
regexStage = "",
|
regexStage = "",
|
||||||
role = "system",
|
role = "system",
|
||||||
regexCollector = null,
|
regexCollector = null,
|
||||||
|
applyMvu = true,
|
||||||
} = {},
|
} = {},
|
||||||
) {
|
) {
|
||||||
const originalText = typeof text === "string" ? text : "";
|
const originalText = typeof text === "string" ? text : "";
|
||||||
const mvuResult = sanitizeMvuContent(originalText, {
|
const mvuResult = applyMvu
|
||||||
mode,
|
? sanitizeMvuContent(originalText, {
|
||||||
blockedContents,
|
mode,
|
||||||
});
|
blockedContents,
|
||||||
|
})
|
||||||
|
: {
|
||||||
|
text: originalText,
|
||||||
|
changed: false,
|
||||||
|
dropped: false,
|
||||||
|
reasons: [],
|
||||||
|
blockedHitCount: 0,
|
||||||
|
artifactRemovedCount: 0,
|
||||||
|
};
|
||||||
const afterMvu = String(mvuResult.text || "");
|
const afterMvu = String(mvuResult.text || "");
|
||||||
const finalText = regexStage
|
const finalText = regexStage
|
||||||
? applyTaskRegex(
|
? applyTaskRegex(
|
||||||
@@ -292,58 +304,277 @@ function sanitizeTaskPromptText(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function sanitizeChatMessageList(
|
function joinStructuredPath(basePath = "", segment = "") {
|
||||||
settings = {},
|
const normalizedSegment = String(segment || "");
|
||||||
taskType,
|
if (!normalizedSegment) {
|
||||||
chatMessages = [],
|
return basePath;
|
||||||
debugState = null,
|
}
|
||||||
regexCollector = null,
|
if (!basePath) {
|
||||||
) {
|
return normalizedSegment.startsWith("[")
|
||||||
if (!Array.isArray(chatMessages) || chatMessages.length === 0) {
|
? normalizedSegment.slice(1, -1)
|
||||||
return [];
|
: normalizedSegment;
|
||||||
|
}
|
||||||
|
return normalizedSegment.startsWith("[")
|
||||||
|
? `${basePath}${normalizedSegment}`
|
||||||
|
: `${basePath}.${normalizedSegment}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function looksLikeMvuStateContainer(value, seen = new WeakSet()) {
|
||||||
|
if (!value || typeof value !== "object") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (seen.has(value)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
seen.add(value);
|
||||||
|
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
return value.some((item) => looksLikeMvuStateContainer(item, seen));
|
||||||
}
|
}
|
||||||
|
|
||||||
return chatMessages
|
const keys = Object.keys(value).map((key) =>
|
||||||
|
String(key || "").trim().toLowerCase(),
|
||||||
|
);
|
||||||
|
if (
|
||||||
|
keys.some((key) =>
|
||||||
|
["stat_data", "display_data", "delta_data", "$internal"].includes(key),
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object.values(value).some((item) =>
|
||||||
|
looksLikeMvuStateContainer(item, seen),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMvuObjectKeyStripReason(key, value) {
|
||||||
|
const normalizedKey = String(key || "").trim().toLowerCase();
|
||||||
|
if (
|
||||||
|
["stat_data", "display_data", "delta_data", "$internal"].includes(
|
||||||
|
normalizedKey,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return "mvu_state_key_removed";
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
["variables", "message_variables", "chat_variables"].includes(normalizedKey) &&
|
||||||
|
looksLikeMvuStateContainer(value)
|
||||||
|
) {
|
||||||
|
return "mvu_variables_container_removed";
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function sanitizeStructuredPromptValue(
|
||||||
|
settings = {},
|
||||||
|
taskType,
|
||||||
|
value,
|
||||||
|
{
|
||||||
|
fieldName = "",
|
||||||
|
path = fieldName,
|
||||||
|
mode = "aggressive",
|
||||||
|
blockedContents = [],
|
||||||
|
regexStage = "",
|
||||||
|
role = "system",
|
||||||
|
debugState = null,
|
||||||
|
regexCollector = null,
|
||||||
|
applyMvu = true,
|
||||||
|
stripMvuContainers = true,
|
||||||
|
seen = new WeakSet(),
|
||||||
|
} = {},
|
||||||
|
) {
|
||||||
|
if (typeof value === "string") {
|
||||||
|
const sanitized = sanitizeTaskPromptText(settings, taskType, value, {
|
||||||
|
mode,
|
||||||
|
blockedContents,
|
||||||
|
regexStage,
|
||||||
|
role,
|
||||||
|
regexCollector,
|
||||||
|
applyMvu,
|
||||||
|
});
|
||||||
|
pushMvuPromptDebugEntry(debugState, {
|
||||||
|
name: path || fieldName,
|
||||||
|
stage: regexStage,
|
||||||
|
...sanitized,
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
value: sanitized.text,
|
||||||
|
changed: Boolean(sanitized.changed || sanitized.dropped),
|
||||||
|
omit:
|
||||||
|
!String(sanitized.text || "").trim() &&
|
||||||
|
String(value || "").trim().length > 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
const sanitizedArray = [];
|
||||||
|
let changed = false;
|
||||||
|
for (let index = 0; index < value.length; index += 1) {
|
||||||
|
const childResult = sanitizeStructuredPromptValue(
|
||||||
|
settings,
|
||||||
|
taskType,
|
||||||
|
value[index],
|
||||||
|
{
|
||||||
|
fieldName,
|
||||||
|
path: joinStructuredPath(path, `[${index}]`),
|
||||||
|
mode,
|
||||||
|
blockedContents,
|
||||||
|
regexStage,
|
||||||
|
role,
|
||||||
|
debugState,
|
||||||
|
regexCollector,
|
||||||
|
applyMvu,
|
||||||
|
stripMvuContainers,
|
||||||
|
seen,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (childResult.omit) {
|
||||||
|
changed = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
sanitizedArray.push(childResult.value);
|
||||||
|
if (childResult.changed) {
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
value: sanitizedArray,
|
||||||
|
changed: changed || sanitizedArray.length !== value.length,
|
||||||
|
omit: value.length > 0 && sanitizedArray.length === 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value && typeof value === "object") {
|
||||||
|
if (seen.has(value)) {
|
||||||
|
return {
|
||||||
|
value,
|
||||||
|
changed: false,
|
||||||
|
omit: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
seen.add(value);
|
||||||
|
|
||||||
|
const originalLooksMvuContainer = looksLikeMvuStateContainer(value);
|
||||||
|
const sanitizedObject = {};
|
||||||
|
let changed = false;
|
||||||
|
let keptEntries = 0;
|
||||||
|
|
||||||
|
for (const [key, entryValue] of Object.entries(value)) {
|
||||||
|
const stripReason = stripMvuContainers
|
||||||
|
? getMvuObjectKeyStripReason(key, entryValue)
|
||||||
|
: "";
|
||||||
|
if (stripReason) {
|
||||||
|
changed = true;
|
||||||
|
pushMvuPromptDebugEntry(debugState, {
|
||||||
|
name: joinStructuredPath(path, key),
|
||||||
|
stage: regexStage,
|
||||||
|
changed: true,
|
||||||
|
dropped: true,
|
||||||
|
reasons: [stripReason],
|
||||||
|
blockedHitCount: 0,
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const childResult = sanitizeStructuredPromptValue(
|
||||||
|
settings,
|
||||||
|
taskType,
|
||||||
|
entryValue,
|
||||||
|
{
|
||||||
|
fieldName,
|
||||||
|
path: joinStructuredPath(path, key),
|
||||||
|
mode,
|
||||||
|
blockedContents,
|
||||||
|
regexStage,
|
||||||
|
role,
|
||||||
|
debugState,
|
||||||
|
regexCollector,
|
||||||
|
applyMvu,
|
||||||
|
stripMvuContainers,
|
||||||
|
seen,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (childResult.omit) {
|
||||||
|
changed = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
sanitizedObject[key] = childResult.value;
|
||||||
|
keptEntries += 1;
|
||||||
|
if (childResult.changed) {
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
value: sanitizedObject,
|
||||||
|
changed,
|
||||||
|
omit: originalLooksMvuContainer && keptEntries === 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
value,
|
||||||
|
changed: false,
|
||||||
|
omit: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function sanitizePromptMessages(
|
||||||
|
settings = {},
|
||||||
|
taskType,
|
||||||
|
messages = [],
|
||||||
|
{
|
||||||
|
blockedContents = [],
|
||||||
|
regexStage = "input.finalPrompt",
|
||||||
|
debugState = null,
|
||||||
|
regexCollector = null,
|
||||||
|
} = {},
|
||||||
|
) {
|
||||||
|
return (Array.isArray(messages) ? messages : [])
|
||||||
.map((message, index) => {
|
.map((message, index) => {
|
||||||
const rawContent =
|
const sanitized = sanitizeStructuredPromptValue(
|
||||||
typeof message === "string"
|
settings,
|
||||||
? message
|
taskType,
|
||||||
: typeof message?.content === "string"
|
message,
|
||||||
? message.content
|
{
|
||||||
: typeof message?.mes === "string"
|
fieldName: "message",
|
||||||
? message.mes
|
path: `message[${index}]`,
|
||||||
: "";
|
mode: "final-safe",
|
||||||
const sanitized = sanitizeTaskPromptText(settings, taskType, rawContent, {
|
blockedContents,
|
||||||
mode: "aggressive",
|
regexStage,
|
||||||
regexStage: "input.recentMessages",
|
role: message?.role || "system",
|
||||||
role: "system",
|
debugState,
|
||||||
regexCollector,
|
regexCollector,
|
||||||
});
|
},
|
||||||
pushMvuPromptDebugEntry(debugState, {
|
);
|
||||||
name: `chatMessages[${index}]`,
|
if (debugState && (sanitized.changed || sanitized.omit)) {
|
||||||
stage: "input.recentMessages",
|
debugState.finalMessageStripCount += 1;
|
||||||
...sanitized,
|
}
|
||||||
});
|
if (sanitized.omit) {
|
||||||
if (!sanitized.text.trim()) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (typeof message === "string") {
|
const executionMessage = createExecutionMessage(
|
||||||
return sanitized.text;
|
sanitized.value?.role || message?.role,
|
||||||
}
|
sanitized.value?.content,
|
||||||
if (message && typeof message === "object") {
|
{
|
||||||
return {
|
source: String(sanitized.value?.source || message?.source || ""),
|
||||||
...message,
|
blockId: String(sanitized.value?.blockId || message?.blockId || ""),
|
||||||
content:
|
blockName: String(
|
||||||
typeof message.content === "string"
|
sanitized.value?.blockName || message?.blockName || "",
|
||||||
? sanitized.text
|
),
|
||||||
: message.content,
|
blockType: String(
|
||||||
mes:
|
sanitized.value?.blockType || message?.blockType || "",
|
||||||
typeof message.mes === "string"
|
),
|
||||||
? sanitized.text
|
sourceKey: String(
|
||||||
: message.mes,
|
sanitized.value?.sourceKey || message?.sourceKey || "",
|
||||||
};
|
),
|
||||||
}
|
injectionMode: String(
|
||||||
return null;
|
sanitized.value?.injectionMode || message?.injectionMode || "",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return executionMessage;
|
||||||
})
|
})
|
||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
}
|
}
|
||||||
@@ -354,39 +585,45 @@ function sanitizePromptContextInputs(
|
|||||||
context = {},
|
context = {},
|
||||||
debugState = null,
|
debugState = null,
|
||||||
regexCollector = null,
|
regexCollector = null,
|
||||||
|
options = {},
|
||||||
) {
|
) {
|
||||||
const sanitizedContext = {
|
const sanitizedContext = {
|
||||||
...context,
|
...context,
|
||||||
};
|
};
|
||||||
|
const {
|
||||||
|
applyMvu = true,
|
||||||
|
stripMvuContainers = applyMvu,
|
||||||
|
} = options || {};
|
||||||
|
|
||||||
for (const fieldName of INPUT_CONTEXT_MVU_FIELDS) {
|
for (const fieldName of INPUT_CONTEXT_MVU_FIELDS) {
|
||||||
const value = sanitizedContext[fieldName];
|
if (!(fieldName in sanitizedContext)) {
|
||||||
if (typeof value !== "string") {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
const value = sanitizedContext[fieldName];
|
||||||
const regexStage = INPUT_REGEX_STAGE_BY_FIELD[fieldName] || "";
|
const regexStage = INPUT_REGEX_STAGE_BY_FIELD[fieldName] || "";
|
||||||
const sanitized = sanitizeTaskPromptText(settings, taskType, value, {
|
const sanitized = sanitizeStructuredPromptValue(
|
||||||
mode: "aggressive",
|
|
||||||
regexStage,
|
|
||||||
role: "system",
|
|
||||||
regexCollector,
|
|
||||||
});
|
|
||||||
sanitizedContext[fieldName] = sanitized.text;
|
|
||||||
pushMvuPromptDebugEntry(debugState, {
|
|
||||||
name: fieldName,
|
|
||||||
stage: regexStage,
|
|
||||||
...sanitized,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Array.isArray(sanitizedContext.chatMessages)) {
|
|
||||||
sanitizedContext.chatMessages = sanitizeChatMessageList(
|
|
||||||
settings,
|
settings,
|
||||||
taskType,
|
taskType,
|
||||||
sanitizedContext.chatMessages,
|
value,
|
||||||
debugState,
|
{
|
||||||
regexCollector,
|
fieldName,
|
||||||
|
path: fieldName,
|
||||||
|
mode: "aggressive",
|
||||||
|
regexStage,
|
||||||
|
role: "system",
|
||||||
|
debugState,
|
||||||
|
regexCollector,
|
||||||
|
applyMvu,
|
||||||
|
stripMvuContainers,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
sanitizedContext[fieldName] = sanitized.omit
|
||||||
|
? Array.isArray(value)
|
||||||
|
? []
|
||||||
|
: typeof value === "string"
|
||||||
|
? ""
|
||||||
|
: null
|
||||||
|
: sanitized.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
return sanitizedContext;
|
return sanitizedContext;
|
||||||
@@ -759,7 +996,19 @@ export async function buildTaskPrompt(settings = {}, taskType, context = {}) {
|
|||||||
const profile = getActiveTaskProfile(settings, taskType);
|
const profile = getActiveTaskProfile(settings, taskType);
|
||||||
const legacyPrompt = getLegacyPromptForTask(settings, taskType);
|
const legacyPrompt = getLegacyPromptForTask(settings, taskType);
|
||||||
const promptRegexInput = { entries: [] };
|
const promptRegexInput = { entries: [] };
|
||||||
|
const worldInfoRegexInput = { entries: [] };
|
||||||
const mvuPromptDebug = createEmptyMvuPromptDebug();
|
const mvuPromptDebug = createEmptyMvuPromptDebug();
|
||||||
|
const worldInfoInputContext = sanitizePromptContextInputs(
|
||||||
|
settings,
|
||||||
|
taskType,
|
||||||
|
context,
|
||||||
|
null,
|
||||||
|
worldInfoRegexInput,
|
||||||
|
{
|
||||||
|
applyMvu: false,
|
||||||
|
stripMvuContainers: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
const sanitizedInputContext = sanitizePromptContextInputs(
|
const sanitizedInputContext = sanitizePromptContextInputs(
|
||||||
settings,
|
settings,
|
||||||
taskType,
|
taskType,
|
||||||
@@ -788,9 +1037,9 @@ export async function buildTaskPrompt(settings = {}, taskType, context = {}) {
|
|||||||
if (worldInfoRequested) {
|
if (worldInfoRequested) {
|
||||||
const worldInfo = await resolveTaskWorldInfo({
|
const worldInfo = await resolveTaskWorldInfo({
|
||||||
settings,
|
settings,
|
||||||
chatMessages: extractWorldInfoChatMessages(sanitizedInputContext),
|
chatMessages: extractWorldInfoChatMessages(worldInfoInputContext),
|
||||||
userMessage: String(sanitizedInputContext.userMessage || ""),
|
userMessage: String(worldInfoInputContext.userMessage || ""),
|
||||||
templateContext: sanitizedInputContext,
|
templateContext: worldInfoInputContext,
|
||||||
});
|
});
|
||||||
const sanitizedWorldInfo = sanitizeWorldInfoContext(
|
const sanitizedWorldInfo = sanitizeWorldInfoContext(
|
||||||
settings,
|
settings,
|
||||||
@@ -959,7 +1208,7 @@ export async function buildTaskPrompt(settings = {}, taskType, context = {}) {
|
|||||||
executionMessages,
|
executionMessages,
|
||||||
privateTaskMessages,
|
privateTaskMessages,
|
||||||
renderedBlocks,
|
renderedBlocks,
|
||||||
regexInput: promptRegexInput,
|
regexInput: mergeRegexCollectors(promptRegexInput, worldInfoRegexInput),
|
||||||
worldInfoResolution,
|
worldInfoResolution,
|
||||||
systemPrompt,
|
systemPrompt,
|
||||||
customMessages,
|
customMessages,
|
||||||
@@ -1019,6 +1268,7 @@ export async function buildTaskPrompt(settings = {}, taskType, context = {}) {
|
|||||||
effectivePath: {
|
effectivePath: {
|
||||||
promptAssembly: "ordered-private-messages",
|
promptAssembly: "ordered-private-messages",
|
||||||
hostInjectionPlan: "diagnostic-plan-only",
|
hostInjectionPlan: "diagnostic-plan-only",
|
||||||
|
worldInfoInputContext: "raw-context-for-trigger-and-ejs",
|
||||||
ejs:
|
ejs:
|
||||||
worldInfoResolution.debug?.ejsRuntimeStatus ||
|
worldInfoResolution.debug?.ejsRuntimeStatus ||
|
||||||
"unknown",
|
"unknown",
|
||||||
@@ -1053,7 +1303,7 @@ export async function buildTaskPrompt(settings = {}, taskType, context = {}) {
|
|||||||
hostInjectionPlan,
|
hostInjectionPlan,
|
||||||
worldInfoResolution,
|
worldInfoResolution,
|
||||||
mvu: result.debug.mvu,
|
mvu: result.debug.mvu,
|
||||||
regexInput: promptRegexInput,
|
regexInput: result.regexInput,
|
||||||
debug: result.debug,
|
debug: result.debug,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1062,7 +1312,11 @@ export async function buildTaskPrompt(settings = {}, taskType, context = {}) {
|
|||||||
|
|
||||||
export function buildTaskLlmPayload(promptBuild = null, fallbackUserPrompt = "") {
|
export function buildTaskLlmPayload(promptBuild = null, fallbackUserPrompt = "") {
|
||||||
const runtimeMvu = promptBuild?.__mvuRuntime || {};
|
const runtimeMvu = promptBuild?.__mvuRuntime || {};
|
||||||
const executionMessages = Array.isArray(promptBuild?.executionMessages)
|
const taskType = String(promptBuild?.debug?.taskType || "");
|
||||||
|
const blockedContents = Array.isArray(runtimeMvu?.blockedContents)
|
||||||
|
? runtimeMvu.blockedContents
|
||||||
|
: [];
|
||||||
|
const rawExecutionMessages = Array.isArray(promptBuild?.executionMessages)
|
||||||
? promptBuild.executionMessages
|
? promptBuild.executionMessages
|
||||||
.map((message) =>
|
.map((message) =>
|
||||||
createExecutionMessage(message.role, message.content, {
|
createExecutionMessage(message.role, message.content, {
|
||||||
@@ -1076,6 +1330,14 @@ export function buildTaskLlmPayload(promptBuild = null, fallbackUserPrompt = "")
|
|||||||
)
|
)
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
: [];
|
: [];
|
||||||
|
const executionMessages = sanitizePromptMessages(
|
||||||
|
{},
|
||||||
|
taskType,
|
||||||
|
rawExecutionMessages,
|
||||||
|
{
|
||||||
|
blockedContents,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const hasUserMessage = executionMessages.some(
|
const hasUserMessage = executionMessages.some(
|
||||||
(message) => message.role === "user",
|
(message) => message.role === "user",
|
||||||
@@ -1086,23 +1348,29 @@ export function buildTaskLlmPayload(promptBuild = null, fallbackUserPrompt = "")
|
|||||||
String(fallbackUserPrompt || ""),
|
String(fallbackUserPrompt || ""),
|
||||||
{
|
{
|
||||||
mode: "final-safe",
|
mode: "final-safe",
|
||||||
blockedContents: Array.isArray(runtimeMvu?.blockedContents)
|
blockedContents,
|
||||||
? runtimeMvu.blockedContents
|
|
||||||
: [],
|
|
||||||
},
|
},
|
||||||
).text;
|
).text;
|
||||||
|
const additionalMessages =
|
||||||
|
executionMessages.length > 0
|
||||||
|
? []
|
||||||
|
: sanitizePromptMessages(
|
||||||
|
{},
|
||||||
|
taskType,
|
||||||
|
Array.isArray(promptBuild?.privateTaskMessages)
|
||||||
|
? promptBuild.privateTaskMessages
|
||||||
|
: [],
|
||||||
|
{
|
||||||
|
blockedContents,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
systemPrompt:
|
systemPrompt:
|
||||||
executionMessages.length > 0 ? "" : String(promptBuild?.systemPrompt || ""),
|
executionMessages.length > 0 ? "" : String(promptBuild?.systemPrompt || ""),
|
||||||
userPrompt: hasUserMessage ? "" : sanitizedFallbackUserPrompt,
|
userPrompt: hasUserMessage ? "" : sanitizedFallbackUserPrompt,
|
||||||
promptMessages: executionMessages,
|
promptMessages: executionMessages,
|
||||||
additionalMessages:
|
additionalMessages,
|
||||||
executionMessages.length > 0
|
|
||||||
? []
|
|
||||||
: Array.isArray(promptBuild?.privateTaskMessages)
|
|
||||||
? promptBuild.privateTaskMessages
|
|
||||||
: [],
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,12 @@ assert.equal(
|
|||||||
),
|
),
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
assert.equal(
|
||||||
|
isLikelyMvuWorldInfoContent(
|
||||||
|
'{"stat_data":{"地点":"学校"},"display_data":{"地点":"教室"}}',
|
||||||
|
),
|
||||||
|
true,
|
||||||
|
);
|
||||||
assert.equal(isLikelyMvuWorldInfoContent("正常世界设定"), false);
|
assert.equal(isLikelyMvuWorldInfoContent("正常世界设定"), false);
|
||||||
|
|
||||||
const aggressive = sanitizeMvuContent(
|
const aggressive = sanitizeMvuContent(
|
||||||
@@ -45,6 +51,16 @@ assert.equal(finalSafe.dropped, false);
|
|||||||
assert.equal(finalSafe.text, "说明文字\n尾巴");
|
assert.equal(finalSafe.text, "说明文字\n尾巴");
|
||||||
assert.deepEqual(finalSafe.reasons, ["artifact_stripped"]);
|
assert.deepEqual(finalSafe.reasons, ["artifact_stripped"]);
|
||||||
|
|
||||||
|
const macroSafe = sanitizeMvuContent(
|
||||||
|
"地点={{get_message_variable::stat_data.地点}}\n<%- SafeGetValue(msg_data.地点) %>",
|
||||||
|
{
|
||||||
|
mode: "final-safe",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
assert.equal(macroSafe.dropped, false);
|
||||||
|
assert.equal(macroSafe.text, "地点=");
|
||||||
|
assert.deepEqual(macroSafe.reasons, ["artifact_stripped"]);
|
||||||
|
|
||||||
const blocked = sanitizeMvuContent("前缀\n被拦截条目\n后缀", {
|
const blocked = sanitizeMvuContent("前缀\n被拦截条目\n后缀", {
|
||||||
mode: "final-safe",
|
mode: "final-safe",
|
||||||
blockedContents: ["被拦截条目"],
|
blockedContents: ["被拦截条目"],
|
||||||
|
|||||||
@@ -65,6 +65,9 @@ const originalExtensionSettings = globalThis.__promptBuilderMvuExtensionSettings
|
|||||||
const originalContext = globalThis.__promptBuilderMvuContext;
|
const originalContext = globalThis.__promptBuilderMvuContext;
|
||||||
const originalSendOpenAIRequest = globalThis.__promptBuilderMvuSendOpenAIRequest;
|
const originalSendOpenAIRequest = globalThis.__promptBuilderMvuSendOpenAIRequest;
|
||||||
const originalFetch = globalThis.fetch;
|
const originalFetch = globalThis.fetch;
|
||||||
|
const originalGetWorldbook = globalThis.getWorldbook;
|
||||||
|
const originalGetLorebookEntries = globalThis.getLorebookEntries;
|
||||||
|
const originalGetCharWorldbookNames = globalThis.getCharWorldbookNames;
|
||||||
|
|
||||||
globalThis.require = require;
|
globalThis.require = require;
|
||||||
globalThis.__promptBuilderMvuExtensionSettings = {
|
globalThis.__promptBuilderMvuExtensionSettings = {
|
||||||
@@ -82,6 +85,38 @@ globalThis.__promptBuilderMvuContext = {
|
|||||||
chatId: "mvu-test-chat",
|
chatId: "mvu-test-chat",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function createWorldbookEntry({
|
||||||
|
uid,
|
||||||
|
name,
|
||||||
|
comment = name,
|
||||||
|
content,
|
||||||
|
strategyType = "constant",
|
||||||
|
keys = [],
|
||||||
|
enabled = true,
|
||||||
|
order = 10,
|
||||||
|
}) {
|
||||||
|
return {
|
||||||
|
uid,
|
||||||
|
name,
|
||||||
|
comment,
|
||||||
|
content,
|
||||||
|
enabled,
|
||||||
|
position: {
|
||||||
|
type: "before_character_definition",
|
||||||
|
role: "system",
|
||||||
|
depth: 0,
|
||||||
|
order,
|
||||||
|
},
|
||||||
|
strategy: {
|
||||||
|
type: strategyType,
|
||||||
|
keys,
|
||||||
|
keys_secondary: { logic: "and_any", keys: [] },
|
||||||
|
},
|
||||||
|
probability: 100,
|
||||||
|
extra: {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const extensionsApi = await import("../../../../extensions.js");
|
const extensionsApi = await import("../../../../extensions.js");
|
||||||
const { createDefaultTaskProfiles } = await import("../prompt-profiles.js");
|
const { createDefaultTaskProfiles } = await import("../prompt-profiles.js");
|
||||||
@@ -150,6 +185,18 @@ try {
|
|||||||
injectionMode: "append",
|
injectionMode: "append",
|
||||||
order: recallProfile.blocks.length,
|
order: recallProfile.blocks.length,
|
||||||
});
|
});
|
||||||
|
recallProfile.blocks.push({
|
||||||
|
id: "mvu-chat-custom",
|
||||||
|
name: "聊天对象检查",
|
||||||
|
type: "custom",
|
||||||
|
enabled: true,
|
||||||
|
role: "system",
|
||||||
|
sourceKey: "",
|
||||||
|
sourceField: "",
|
||||||
|
content: "聊天对象 {{chatMessages}}",
|
||||||
|
injectionMode: "append",
|
||||||
|
order: recallProfile.blocks.length,
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
llmApiUrl: "https://example.com/v1",
|
llmApiUrl: "https://example.com/v1",
|
||||||
@@ -171,20 +218,62 @@ try {
|
|||||||
userPersona: "变量更新规则:\ntype: state\n当前时间: 12:00",
|
userPersona: "变量更新规则:\ntype: state\n当前时间: 12:00",
|
||||||
recentMessages:
|
recentMessages:
|
||||||
"最近消息 <status_current_variable>hp=3</status_current_variable> BAD_RECENT",
|
"最近消息 <status_current_variable>hp=3</status_current_variable> BAD_RECENT",
|
||||||
|
chatMessages: [
|
||||||
|
{
|
||||||
|
role: "assistant",
|
||||||
|
content: "聊天内容 BAD_RECENT",
|
||||||
|
variables: {
|
||||||
|
0: {
|
||||||
|
stat_data: { hp: [3, "状态更新"] },
|
||||||
|
display_data: { hp: "2->3" },
|
||||||
|
delta_data: { hp: "2->3" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
debugStatus: "{{get_message_variable::display_data.hp}} BAD_RECENT",
|
||||||
|
},
|
||||||
|
],
|
||||||
userMessage:
|
userMessage:
|
||||||
"用户输入 <updatevariable>secret</updatevariable> BAD_USER",
|
"用户输入 <updatevariable>secret</updatevariable> {{get_message_variable::stat_data.hp}} BAD_USER",
|
||||||
candidateNodes: "候选节点 BAD_CANDIDATE",
|
candidateNodes: [
|
||||||
candidateText: "候选节点 BAD_CANDIDATE",
|
{
|
||||||
|
id: "node-1",
|
||||||
|
summary: "候选节点 BAD_CANDIDATE <StatusPlaceHolderImpl/>",
|
||||||
|
variables: {
|
||||||
|
0: {
|
||||||
|
stat_data: { 地点: "学校" },
|
||||||
|
display_data: { 地点: "教室" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
note: "{{get_message_variable::stat_data.地点}} BAD_CANDIDATE",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
candidateText:
|
||||||
|
"候选节点 BAD_CANDIDATE {{get_message_variable::stat_data.地点}}",
|
||||||
graphStats: "candidate_count=1",
|
graphStats: "candidate_count=1",
|
||||||
});
|
});
|
||||||
|
|
||||||
assert.match(promptBuild.systemPrompt, /GOOD_RECENT/);
|
assert.match(promptBuild.systemPrompt, /GOOD_RECENT/);
|
||||||
assert.match(JSON.stringify(promptBuild.executionMessages), /GOOD_USER/);
|
|
||||||
assert.match(JSON.stringify(promptBuild.executionMessages), /GOOD_CANDIDATE/);
|
assert.match(JSON.stringify(promptBuild.executionMessages), /GOOD_CANDIDATE/);
|
||||||
assert.match(promptBuild.systemPrompt, /FINAL_GOOD/);
|
assert.match(promptBuild.systemPrompt, /FINAL_GOOD/);
|
||||||
|
assert.equal(
|
||||||
|
promptBuild.debug.mvu.sanitizedFields.some((entry) => entry.name === "userMessage"),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
promptBuild.debug.mvu.sanitizedFields.some((entry) =>
|
||||||
|
String(entry.name || "").startsWith("candidateNodes[0].variables"),
|
||||||
|
),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
promptBuild.debug.mvu.sanitizedFields.some((entry) =>
|
||||||
|
String(entry.name || "").startsWith("chatMessages[0].variables"),
|
||||||
|
),
|
||||||
|
true,
|
||||||
|
);
|
||||||
assert.doesNotMatch(
|
assert.doesNotMatch(
|
||||||
JSON.stringify(promptBuild),
|
JSON.stringify(promptBuild),
|
||||||
/status_current_variable|updatevariable|StatusPlaceHolderImpl/i,
|
/status_current_variable|updatevariable|StatusPlaceHolderImpl|stat_data|display_data|delta_data|get_message_variable/i,
|
||||||
);
|
);
|
||||||
assert.equal(promptBuild.debug.mvu.sanitizedFieldCount >= 4, true);
|
assert.equal(promptBuild.debug.mvu.sanitizedFieldCount >= 4, true);
|
||||||
assert.equal(promptBuild.debug.mvu.finalMessageStripCount >= 1, true);
|
assert.equal(promptBuild.debug.mvu.finalMessageStripCount >= 1, true);
|
||||||
@@ -234,6 +323,112 @@ try {
|
|||||||
);
|
);
|
||||||
assert.equal(systemOnlyPayload.userPrompt, "fallback text");
|
assert.equal(systemOnlyPayload.userPrompt, "fallback text");
|
||||||
|
|
||||||
|
const rawWorldInfoEntries = [
|
||||||
|
createWorldbookEntry({
|
||||||
|
uid: 101,
|
||||||
|
name: "raw-trigger",
|
||||||
|
comment: "原始触发命中",
|
||||||
|
content: "世界书原始触发成功。",
|
||||||
|
strategyType: "selective",
|
||||||
|
keys: ["星火密令"],
|
||||||
|
order: 10,
|
||||||
|
}),
|
||||||
|
createWorldbookEntry({
|
||||||
|
uid: 102,
|
||||||
|
name: "raw-ejs",
|
||||||
|
comment: "原始 EJS 命中",
|
||||||
|
content:
|
||||||
|
'<%= user_input.includes("星火密令") ? "EJS 看到了原始 MVU 信号。" : "EJS 丢失了原始 MVU 信号。" %>',
|
||||||
|
order: 20,
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
globalThis.getCharWorldbookNames = () => ({
|
||||||
|
primary: "mvu-raw-worldbook",
|
||||||
|
additional: [],
|
||||||
|
});
|
||||||
|
globalThis.getWorldbook = async (worldbookName) =>
|
||||||
|
worldbookName === "mvu-raw-worldbook" ? rawWorldInfoEntries : [];
|
||||||
|
globalThis.getLorebookEntries = async (worldbookName) =>
|
||||||
|
(worldbookName === "mvu-raw-worldbook" ? rawWorldInfoEntries : []).map(
|
||||||
|
(entry) => ({
|
||||||
|
uid: entry.uid,
|
||||||
|
comment: entry.comment,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
globalThis.__promptBuilderMvuContext = {
|
||||||
|
...globalThis.__promptBuilderMvuContext,
|
||||||
|
chatId: "mvu-raw-trigger-chat",
|
||||||
|
chatMetadata: {},
|
||||||
|
extensionSettings: {},
|
||||||
|
powerUserSettings: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
const rawWorldInfoSettings = buildSettings();
|
||||||
|
rawWorldInfoSettings.taskProfiles.recall = {
|
||||||
|
activeProfileId: "raw-worldinfo",
|
||||||
|
profiles: [
|
||||||
|
{
|
||||||
|
id: "raw-worldinfo",
|
||||||
|
name: "raw worldinfo",
|
||||||
|
taskType: "recall",
|
||||||
|
builtin: false,
|
||||||
|
blocks: [
|
||||||
|
{
|
||||||
|
id: "wi-before",
|
||||||
|
name: "世界书前块",
|
||||||
|
type: "builtin",
|
||||||
|
enabled: true,
|
||||||
|
role: "system",
|
||||||
|
sourceKey: "worldInfoBefore",
|
||||||
|
sourceField: "",
|
||||||
|
content: "",
|
||||||
|
injectionMode: "append",
|
||||||
|
order: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "recent-messages",
|
||||||
|
name: "最近消息",
|
||||||
|
type: "builtin",
|
||||||
|
enabled: true,
|
||||||
|
role: "system",
|
||||||
|
sourceKey: "recentMessages",
|
||||||
|
sourceField: "",
|
||||||
|
content: "",
|
||||||
|
injectionMode: "append",
|
||||||
|
order: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
generation: createDefaultTaskProfiles().recall.profiles[0].generation,
|
||||||
|
regex: {
|
||||||
|
enabled: false,
|
||||||
|
inheritStRegex: false,
|
||||||
|
stages: {},
|
||||||
|
localRules: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const rawWorldInfoPromptBuild = await buildTaskPrompt(rawWorldInfoSettings, "recall", {
|
||||||
|
taskName: "recall",
|
||||||
|
recentMessages: "最近消息",
|
||||||
|
userMessage:
|
||||||
|
"继续 <status_current_variable>星火密令</status_current_variable>",
|
||||||
|
chatMessages: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.match(rawWorldInfoPromptBuild.systemPrompt, /世界书原始触发成功/);
|
||||||
|
assert.match(rawWorldInfoPromptBuild.systemPrompt, /EJS 看到了原始 MVU 信号/);
|
||||||
|
assert.doesNotMatch(
|
||||||
|
rawWorldInfoPromptBuild.systemPrompt,
|
||||||
|
/status_current_variable/i,
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
rawWorldInfoPromptBuild.debug.effectivePath?.worldInfoInputContext,
|
||||||
|
"raw-context-for-trigger-and-ejs",
|
||||||
|
);
|
||||||
|
|
||||||
const capturedBodies = [];
|
const capturedBodies = [];
|
||||||
globalThis.fetch = async (_url, options = {}) => {
|
globalThis.fetch = async (_url, options = {}) => {
|
||||||
capturedBodies.push(JSON.parse(String(options.body || "{}")));
|
capturedBodies.push(JSON.parse(String(options.body || "{}")));
|
||||||
@@ -273,7 +468,7 @@ try {
|
|||||||
assert.equal(capturedBodies.length, 1);
|
assert.equal(capturedBodies.length, 1);
|
||||||
assert.doesNotMatch(
|
assert.doesNotMatch(
|
||||||
JSON.stringify(capturedBodies[0].messages),
|
JSON.stringify(capturedBodies[0].messages),
|
||||||
/status_current_variable|updatevariable|StatusPlaceHolderImpl/i,
|
/status_current_variable|updatevariable|StatusPlaceHolderImpl|stat_data|display_data|delta_data|get_message_variable/i,
|
||||||
);
|
);
|
||||||
|
|
||||||
const runtimePromptBuild =
|
const runtimePromptBuild =
|
||||||
@@ -285,15 +480,15 @@ try {
|
|||||||
assert.ok(runtimeLlmRequest);
|
assert.ok(runtimeLlmRequest);
|
||||||
assert.doesNotMatch(
|
assert.doesNotMatch(
|
||||||
JSON.stringify(runtimePromptBuild.executionMessages),
|
JSON.stringify(runtimePromptBuild.executionMessages),
|
||||||
/status_current_variable|updatevariable|StatusPlaceHolderImpl/i,
|
/status_current_variable|updatevariable|StatusPlaceHolderImpl|stat_data|display_data|delta_data|get_message_variable/i,
|
||||||
);
|
);
|
||||||
assert.doesNotMatch(
|
assert.doesNotMatch(
|
||||||
JSON.stringify(runtimeLlmRequest.messages),
|
JSON.stringify(runtimeLlmRequest.messages),
|
||||||
/status_current_variable|updatevariable|StatusPlaceHolderImpl/i,
|
/status_current_variable|updatevariable|StatusPlaceHolderImpl|stat_data|display_data|delta_data|get_message_variable/i,
|
||||||
);
|
);
|
||||||
assert.doesNotMatch(
|
assert.doesNotMatch(
|
||||||
JSON.stringify(runtimeLlmRequest.requestBody?.messages || []),
|
JSON.stringify(runtimeLlmRequest.requestBody?.messages || []),
|
||||||
/status_current_variable|updatevariable|StatusPlaceHolderImpl/i,
|
/status_current_variable|updatevariable|StatusPlaceHolderImpl|stat_data|display_data|delta_data|get_message_variable/i,
|
||||||
);
|
);
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
runtimeLlmRequest.messages,
|
runtimeLlmRequest.messages,
|
||||||
@@ -331,4 +526,22 @@ try {
|
|||||||
}
|
}
|
||||||
|
|
||||||
globalThis.fetch = originalFetch;
|
globalThis.fetch = originalFetch;
|
||||||
|
|
||||||
|
if (originalGetWorldbook === undefined) {
|
||||||
|
delete globalThis.getWorldbook;
|
||||||
|
} else {
|
||||||
|
globalThis.getWorldbook = originalGetWorldbook;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (originalGetLorebookEntries === undefined) {
|
||||||
|
delete globalThis.getLorebookEntries;
|
||||||
|
} else {
|
||||||
|
globalThis.getLorebookEntries = originalGetLorebookEntries;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (originalGetCharWorldbookNames === undefined) {
|
||||||
|
delete globalThis.getCharWorldbookNames;
|
||||||
|
} else {
|
||||||
|
globalThis.getCharWorldbookNames = originalGetCharWorldbookNames;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user