Update formatMessages.mjs
This commit is contained in:
+127
-32
@@ -18,10 +18,7 @@ export function formatMessages(messages, proxyModel, randomFileName) {
|
|||||||
// 如果启用 clewd
|
// 如果启用 clewd
|
||||||
const CLEWD_ENABLED = process.env.CLEWD_ENABLED === 'true';
|
const CLEWD_ENABLED = process.env.CLEWD_ENABLED === 'true';
|
||||||
if (CLEWD_ENABLED) {
|
if (CLEWD_ENABLED) {
|
||||||
messages = messages.map(message => {
|
messages = xmlPlotAllMessages(messages, roleFeatures);
|
||||||
message.content = xmlPlot(message.content, roleFeatures);
|
|
||||||
return message;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasAIRound0 = messages.some(message => message.content.includes('<!-- AI Round 0 begins. -->'));
|
const hasAIRound0 = messages.some(message => message.content.includes('<!-- AI Round 0 begins. -->'));
|
||||||
@@ -190,7 +187,7 @@ function removeCustomRoleDefinitions(messages) {
|
|||||||
|
|
||||||
// 转换角色
|
// 转换角色
|
||||||
function convertRoles(messages, roleFeatures) {
|
function convertRoles(messages, roleFeatures) {
|
||||||
const { systemRole, userRole, assistantRole } = roleFeatures;
|
const {systemRole, userRole, assistantRole} = roleFeatures;
|
||||||
const roleMap = {
|
const roleMap = {
|
||||||
'system': systemRole,
|
'system': systemRole,
|
||||||
'user': userRole,
|
'user': userRole,
|
||||||
@@ -207,7 +204,7 @@ function convertRoles(messages, roleFeatures) {
|
|||||||
} else {
|
} else {
|
||||||
const roleKey = currentRole.toLowerCase();
|
const roleKey = currentRole.toLowerCase();
|
||||||
const newRole = roleMap[roleKey] || currentRole;
|
const newRole = roleMap[roleKey] || currentRole;
|
||||||
return { ...message, role: newRole };
|
return {...message, role: newRole};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -253,6 +250,42 @@ function replaceRolesInContent(messages, roleFeatures) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* xmlPlotAllMessages(messages, roleFeatures, options)
|
||||||
|
*
|
||||||
|
* 在 CLEWD 阶段,对一组 messages 进行二次处理:
|
||||||
|
* 默认将所有消息的 role 设置为空字符串(可以视为“去掉角色”)。
|
||||||
|
* 若 message.content 中含有 <|KEEP_ROLE|> 标记,则保留原先的 role,不置空。
|
||||||
|
* 通过 options.skipSystem = true/false,决定是否跳过 system 段落处理。
|
||||||
|
* 调用 xmlPlot(...) 时传入 apiKey / skipSystem 等参数,以进一步根据老版逻辑进行区分。
|
||||||
|
*
|
||||||
|
* @param {Array} messages - [{ role: 'user'/..., content: '...' }, ...]
|
||||||
|
* @param {object} roleFeatures - { systemRole, userRole, assistantRole, prefix }
|
||||||
|
* @return {Array} 新的 messages(content 已经过多轮正则处理)
|
||||||
|
* -----------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
export function xmlPlotAllMessages(messages, roleFeatures) {
|
||||||
|
return messages.map(msg => {
|
||||||
|
if (!msg.content.includes('<|KEEP_ROLE|>')) {
|
||||||
|
msg = {
|
||||||
|
...msg,
|
||||||
|
role: '' // 置空
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const newContent = xmlPlot(msg.content, roleFeatures);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...msg,
|
||||||
|
content: newContent
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对单条文本 content 进行多轮正则及合并处理
|
||||||
|
* @param {string} content - 消息体文本
|
||||||
|
* @param {object} roleFeatures - { systemRole, userRole, assistantRole, prefix }
|
||||||
|
*/
|
||||||
function xmlPlot(content, roleFeatures) {
|
function xmlPlot(content, roleFeatures) {
|
||||||
let regexLog = '';
|
let regexLog = '';
|
||||||
// 第一次正则替换
|
// 第一次正则替换
|
||||||
@@ -267,19 +300,20 @@ function xmlPlot(content, roleFeatures) {
|
|||||||
};
|
};
|
||||||
content = xmlPlot_merge(content, mergeTag, roleFeatures);
|
content = xmlPlot_merge(content, mergeTag, roleFeatures);
|
||||||
|
|
||||||
// 自定义插入处理
|
// 处理内嵌 <@N> ... </@N> 插入
|
||||||
const escapeRegExp = (string) => string.replace(/[\b.*+?^${}()|[\]\\]/g, '\\$&');
|
const escapeRegExp = (str) => str.replace(/[\b.*+?^${}()|[\]\\]/g, '\\$&');
|
||||||
const humanLabelRaw = `${roleFeatures.userRole}:`;
|
const humanLabelRaw = `${roleFeatures.userRole}:`;
|
||||||
const assistantLabelRaw = `${roleFeatures.assistantRole}:`;
|
const assistantLabelRaw = `${roleFeatures.assistantRole}:`;
|
||||||
const humanLabel = escapeRegExp(humanLabelRaw);
|
const humanLabel = escapeRegExp(humanLabelRaw);
|
||||||
const assistantLabel = escapeRegExp(assistantLabelRaw);
|
const assistantLabel = escapeRegExp(assistantLabelRaw);
|
||||||
|
|
||||||
|
// 根据段落分隔符拆分
|
||||||
let splitContent = content.split(new RegExp(`\\n\\n(?=${humanLabel}|${assistantLabel})`, 'g'));
|
let splitContent = content.split(new RegExp(`\\n\\n(?=${humanLabel}|${assistantLabel})`, 'g'));
|
||||||
let match;
|
let match;
|
||||||
while ((match = /<@(\d+)>(.*?)<\/@\1>/gs.exec(content)) !== null) {
|
while ((match = /<@(\d+)>(.*?)<\/@\1>/gs.exec(content)) !== null) {
|
||||||
let index = splitContent.length - parseInt(match[1]) - 1;
|
let insertionIndex = splitContent.length - parseInt(match[1], 10) - 1;
|
||||||
if (index >= 0) {
|
if (insertionIndex >= 0) {
|
||||||
splitContent[index] += '\n\n' + match[2];
|
splitContent[insertionIndex] += '\n\n' + match[2];
|
||||||
}
|
}
|
||||||
content = content.replace(match[0], '');
|
content = content.replace(match[0], '');
|
||||||
}
|
}
|
||||||
@@ -295,62 +329,123 @@ function xmlPlot(content, roleFeatures) {
|
|||||||
const humanLabelPattern = new RegExp(`\\n\\n${humanLabel}`, 'g');
|
const humanLabelPattern = new RegExp(`\\n\\n${humanLabel}`, 'g');
|
||||||
let segcontentHuman = content.split(humanLabelPattern);
|
let segcontentHuman = content.split(humanLabelPattern);
|
||||||
let segcontentlastIndex = segcontentHuman.length - 1;
|
let segcontentlastIndex = segcontentHuman.length - 1;
|
||||||
if (segcontentlastIndex >= 2 && segcontentHuman[segcontentlastIndex].includes('<|Plain Prompt Enable|>') && !content.includes(`\n\nPlainPrompt:`)) {
|
if (
|
||||||
content = segcontentHuman.slice(0, segcontentlastIndex).join(`\n\n${humanLabelRaw}`) + `\n\nPlainPrompt:` + segcontentHuman.slice(segcontentlastIndex).join(`\n\n${humanLabelRaw}`).replace(new RegExp(`\\n\\n${humanLabel}\\s*PlainPrompt:`, 'g'), '\n\nPlainPrompt:');
|
segcontentlastIndex >= 2 &&
|
||||||
|
segcontentHuman[segcontentlastIndex].includes('<|Plain Prompt Enable|>') &&
|
||||||
|
!content.includes(`\n\nPlainPrompt:`)
|
||||||
|
) {
|
||||||
|
content = segcontentHuman
|
||||||
|
.slice(0, segcontentlastIndex)
|
||||||
|
.join(`\n\n${humanLabelRaw}`) +
|
||||||
|
`\n\nPlainPrompt:` +
|
||||||
|
segcontentHuman
|
||||||
|
.slice(segcontentlastIndex)
|
||||||
|
.join(`\n\n${humanLabelRaw}`)
|
||||||
|
.replace(new RegExp(`\\n\\n${humanLabel}\\s*PlainPrompt:`, 'g'), '\n\nPlainPrompt:');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 第三次正则替换
|
// 第三次正则替换
|
||||||
content = xmlPlot_regex(content, 3, regexLog);
|
content = xmlPlot_regex(content, 3, regexLog);
|
||||||
|
|
||||||
// 清理和格式化
|
// 清理和格式化
|
||||||
content = content.replace(/<regex( +order *= *\d)?>.*?<\/regex>/gm, '')
|
content = content
|
||||||
|
// 移除剩余 <regex ...> 包裹
|
||||||
|
.replace(/<regex( +order *= *\d)?>.*?<\/regex>/gm, '')
|
||||||
|
// 统一换行
|
||||||
.replace(/\r\n|\r/gm, '\n')
|
.replace(/\r\n|\r/gm, '\n')
|
||||||
|
// <|curtail|> 替换为换行
|
||||||
.replace(/\s*<\|curtail\|>\s*/g, '\n')
|
.replace(/\s*<\|curtail\|>\s*/g, '\n')
|
||||||
|
// <|join|> 去掉
|
||||||
.replace(/\s*<\|join\|>\s*/g, '')
|
.replace(/\s*<\|join\|>\s*/g, '')
|
||||||
|
// <|space|> 替换为" "
|
||||||
.replace(/\s*<\|space\|>\s*/g, ' ')
|
.replace(/\s*<\|space\|>\s*/g, ' ')
|
||||||
|
// 修正多余的空格/换行
|
||||||
.replace(new RegExp(`\\s*\\n\\n(${humanLabel}|${assistantLabel})\\s+`, 'g'), '\n\n$1 ')
|
.replace(new RegExp(`\\s*\\n\\n(${humanLabel}|${assistantLabel})\\s+`, 'g'), '\n\n$1 ')
|
||||||
.replace(/<\|(\\.*?)\|>/g, function (match, p1) {
|
// 对 <|xxx|> 做 JSON.parse 反序列化
|
||||||
|
.replace(/<\|(\\.*?)\|>/g, function (m, p1) {
|
||||||
try {
|
try {
|
||||||
return JSON.parse(`"${p1.replace(/\\?"/g, '\\"')}"`);
|
return JSON.parse(`"${p1.replace(/\\?"/g, '\\"')}"`);
|
||||||
} catch { return match }
|
} catch {
|
||||||
});
|
return m;
|
||||||
|
}
|
||||||
// 确保格式正确
|
})
|
||||||
content = content.replace(/\s*<\|(?!padtxt).*?\|>\s*/g, '\n\n').trim()
|
// 最后去掉多余
|
||||||
|
.replace(/\s*<\|(?!padtxt).*?\|>\s*/g, '\n\n')
|
||||||
|
.trim()
|
||||||
.replace(/(?<=\n)\n(?=\n)/g, '');
|
.replace(/(?<=\n)\n(?=\n)/g, '');
|
||||||
|
|
||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析 <regex>"/pattern/flags":"replacement"</regex> 标签并执行替换
|
||||||
|
* @param {string} content
|
||||||
|
* @param {number} order
|
||||||
|
* @param {string} regexLog
|
||||||
|
* @returns {string} 替换后的文本
|
||||||
|
*/
|
||||||
function xmlPlot_regex(content, order, regexLog) {
|
function xmlPlot_regex(content, order, regexLog) {
|
||||||
let matches = content.match(new RegExp(`<regex(?: +order *= *${order})?> *"(/?)(.*?)\\1(.*?)" *: *"(.*?)" *</regex>`, 'gm'));
|
// 只匹配与当前 order 相符的 <regex> 标签
|
||||||
if (matches) {
|
const patternRegex = new RegExp(
|
||||||
matches.forEach(match => {
|
`<regex(?: +order *= *(${order}))?>\\s*"\\/([^"]*?)\\/([gimsyu]*)"\\s*:\\s*"(.*?)"\\s*<\\/regex>`,
|
||||||
|
'gm'
|
||||||
|
);
|
||||||
|
|
||||||
|
let match;
|
||||||
|
while ((match = patternRegex.exec(content)) !== null) {
|
||||||
|
// match[0] : <regex>...</regex>
|
||||||
|
// match[1] : order
|
||||||
|
// match[2] : pattern
|
||||||
|
// match[3] : flags
|
||||||
|
// match[4] : replacement
|
||||||
|
|
||||||
|
const fullBlock = match[0];
|
||||||
|
const subPattern = match[2];
|
||||||
|
const subFlags = match[3];
|
||||||
|
let replacement = match[4];
|
||||||
|
|
||||||
|
regexLog += fullBlock + '\n';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const reg = /<regex(?: +order *= *\d)?> *"(\/?)(.*?)\1(.*?)" *: *"(.*?)" *<\/regex>/.exec(match);
|
// 构造 JS 正则
|
||||||
regexLog += match + '\n';
|
const regObj = new RegExp(subPattern, subFlags);
|
||||||
content = content.replace(new RegExp(reg[2], reg[3]), JSON.parse(`"${reg[4].replace(/\\?"/g, '\\"')}"`));
|
// 反序列化 replacement
|
||||||
|
replacement = JSON.parse(`"${replacement.replace(/\\?"/g, '\\"')}"`);
|
||||||
|
// 执行替换
|
||||||
|
content = content.replace(regObj, replacement);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(`Regex error: ` + match + '\n' + err);
|
console.log(`Regex error: ` + fullBlock + '\n' + err);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 多段 "Human:..." 或 "Assistant:..." 合并
|
||||||
|
* @param {string} content
|
||||||
|
* @param {*} mergeTag { all, system, human, assistant }
|
||||||
|
* @param {*} roleFeatures
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
function xmlPlot_merge(content, mergeTag, roleFeatures) {
|
function xmlPlot_merge(content, mergeTag, roleFeatures) {
|
||||||
const escapeRegExp = (string) => string.replace(/[\b.*+?^${}()|[\]\\]/g, '\\$&');
|
const escapeRegExp = (str) => str.replace(/[\b.*+?^${}()|[\]\\]/g, '\\$&');
|
||||||
|
|
||||||
const humanLabelRaw = `${roleFeatures.userRole}:`;
|
const humanLabelRaw = `${roleFeatures.userRole}:`;
|
||||||
const assistantLabelRaw = `${roleFeatures.assistantRole}:`;
|
const assistantLabelRaw = `${roleFeatures.assistantRole}:`;
|
||||||
const humanLabel = escapeRegExp(humanLabelRaw);
|
const humanLabel = escapeRegExp(humanLabelRaw);
|
||||||
const assistantLabel = escapeRegExp(assistantLabelRaw);
|
const assistantLabel = escapeRegExp(assistantLabelRaw);
|
||||||
|
|
||||||
|
// 如果出现 xmlPlot:
|
||||||
if (/(\n\n|^\s*)xmlPlot:\s*/.test(content)) {
|
if (/(\n\n|^\s*)xmlPlot:\s*/.test(content)) {
|
||||||
content = content.replace(/(\n\n|^\s*)xmlPlot: */g, mergeTag.system && mergeTag.human && mergeTag.all ? `\n\n${humanLabelRaw} ` : '$1');
|
content = content.replace(
|
||||||
|
/(\n\n|^\s*)xmlPlot:\s*/g,
|
||||||
|
mergeTag.system && mergeTag.human && mergeTag.all
|
||||||
|
? `\n\n${humanLabelRaw} `
|
||||||
|
: '$1'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 合并 Human 段落
|
// 合并 Human 段
|
||||||
if (mergeTag.all && mergeTag.human) {
|
if (mergeTag.all && mergeTag.human) {
|
||||||
const humanRegex = new RegExp(`(?:\\n\\n|^\\s*)${humanLabel}(.*?)(?=\\n\\n(?:${assistantLabel}|$))`, 'gs');
|
const humanRegex = new RegExp(`(?:\\n\\n|^\\s*)${humanLabel}(.*?)(?=\\n\\n(?:${assistantLabel}|$))`, 'gs');
|
||||||
content = content.replace(humanRegex, (match, p1) => {
|
content = content.replace(humanRegex, (match, p1) => {
|
||||||
@@ -359,7 +454,7 @@ function xmlPlot_merge(content, mergeTag, roleFeatures) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 合并 Assistant 段落
|
// 合并 Assistant 段
|
||||||
if (mergeTag.all && mergeTag.assistant) {
|
if (mergeTag.all && mergeTag.assistant) {
|
||||||
const assistantRegex = new RegExp(`(?:\\n\\n|^\\s*)${assistantLabel}(.*?)(?=\\n\\n(?:${humanLabel}|$))`, 'gs');
|
const assistantRegex = new RegExp(`(?:\\n\\n|^\\s*)${assistantLabel}(.*?)(?=\\n\\n(?:${humanLabel}|$))`, 'gs');
|
||||||
content = content.replace(assistantRegex, (match, p1) => {
|
content = content.replace(assistantRegex, (match, p1) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user