mirror of
https://github.com/Youzini-afk/ST-Bionic-Memory-Ecology.git
synced 2026-05-15 22:30:38 +08:00
Merge branch 'main' into main
This commit is contained in:
@@ -6,6 +6,6 @@
|
||||
"js": "index.js",
|
||||
"css": "style.css",
|
||||
"author": "Youzini",
|
||||
"version": "4.2.3",
|
||||
"version": "4.2.2",
|
||||
"homePage": "https://github.com/Youzini-afk/ST-Bionic-Memory-Ecology"
|
||||
}
|
||||
|
||||
@@ -269,6 +269,14 @@ function messageUsesWorldInfoContent(message = {}) {
|
||||
return String(message?.source || "") === "worldInfo-atDepth";
|
||||
}
|
||||
|
||||
function getOptionalFiniteNumber(value) {
|
||||
if (value === null || value === undefined || value === "") {
|
||||
return null;
|
||||
}
|
||||
const numericValue = Number(value);
|
||||
return Number.isFinite(numericValue) ? numericValue : null;
|
||||
}
|
||||
|
||||
function getPromptMessageLikeDescriptor(value) {
|
||||
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
||||
return null;
|
||||
@@ -279,7 +287,7 @@ function getPromptMessageLikeDescriptor(value) {
|
||||
return {
|
||||
content: String(value.content || ""),
|
||||
role: role === "user" ? "user" : "assistant",
|
||||
seq: Number.isFinite(Number(value.seq)) ? Number(value.seq) : null,
|
||||
seq: getOptionalFiniteNumber(value.seq),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -287,7 +295,7 @@ function getPromptMessageLikeDescriptor(value) {
|
||||
return {
|
||||
content: String(value.mes || ""),
|
||||
role: value.is_user === true ? "user" : "assistant",
|
||||
seq: Number.isFinite(Number(value.seq)) ? Number(value.seq) : null,
|
||||
seq: getOptionalFiniteNumber(value.seq),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1072,6 +1080,41 @@ function sortInjectionEntries(entries = []) {
|
||||
});
|
||||
}
|
||||
|
||||
function sortAtDepthInjectionEntries(entries = []) {
|
||||
return [...entries].sort((left, right) => {
|
||||
const depthLeft = Number.isFinite(Number(left?.depth))
|
||||
? Number(left.depth)
|
||||
: 0;
|
||||
const depthRight = Number.isFinite(Number(right?.depth))
|
||||
? Number(right.depth)
|
||||
: 0;
|
||||
const orderLeft = Number.isFinite(Number(left?.order))
|
||||
? Number(left.order)
|
||||
: 0;
|
||||
const orderRight = Number.isFinite(Number(right?.order))
|
||||
? Number(right.order)
|
||||
: 0;
|
||||
const uidLeft = Number.isFinite(Number(left?.uid))
|
||||
? Number(left.uid)
|
||||
: Number.NEGATIVE_INFINITY;
|
||||
const uidRight = Number.isFinite(Number(right?.uid))
|
||||
? Number(right.uid)
|
||||
: Number.NEGATIVE_INFINITY;
|
||||
const indexLeft = Number.isFinite(Number(left?.index))
|
||||
? Number(left.index)
|
||||
: 0;
|
||||
const indexRight = Number.isFinite(Number(right?.index))
|
||||
? Number(right.index)
|
||||
: 0;
|
||||
return (
|
||||
depthRight - depthLeft ||
|
||||
orderLeft - orderRight ||
|
||||
uidRight - uidLeft ||
|
||||
indexLeft - indexRight
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function createHostInjectionPlanEntry(block = {}, position, extra = {}) {
|
||||
return {
|
||||
source: "block",
|
||||
@@ -1154,10 +1197,98 @@ function buildHostInjectionPlan(renderedBlocks = [], worldInfoResolution = {}) {
|
||||
return {
|
||||
before: sortInjectionEntries(plan.before),
|
||||
after: sortInjectionEntries(plan.after),
|
||||
atDepth: sortInjectionEntries(plan.atDepth),
|
||||
atDepth: sortAtDepthInjectionEntries(plan.atDepth),
|
||||
};
|
||||
}
|
||||
|
||||
function createInjectedAtDepthChatMessage(message = {}) {
|
||||
const descriptor = getPromptMessageLikeDescriptor(message);
|
||||
if (!descriptor) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
...(message && typeof message === "object" ? message : {}),
|
||||
role: descriptor.role,
|
||||
content: descriptor.content,
|
||||
seq: descriptor.seq,
|
||||
uid: Number.isFinite(Number(message?.uid))
|
||||
? Number(message.uid)
|
||||
: null,
|
||||
index: Number.isFinite(Number(message?.index))
|
||||
? Number(message.index)
|
||||
: null,
|
||||
name: String(message?.name || ""),
|
||||
sourceName: String(message?.sourceName || ""),
|
||||
worldbook: String(message?.worldbook || ""),
|
||||
source: String(message?.source || "worldInfo-atDepth"),
|
||||
sourceKey: String(message?.sourceKey || "taskAdditionalMessages"),
|
||||
derivedFromWorldInfo: true,
|
||||
contentOrigin:
|
||||
String(message?.contentOrigin || "") ||
|
||||
PROMPT_CONTENT_ORIGIN.WORLD_INFO_RENDERED,
|
||||
sanitizationEligible: message?.sanitizationEligible === true,
|
||||
regexSourceType: String(message?.regexSourceType || "world_info"),
|
||||
depth: Number.isFinite(Number(message?.depth))
|
||||
? Number(message.depth)
|
||||
: 0,
|
||||
order: Number.isFinite(Number(message?.order))
|
||||
? Number(message.order)
|
||||
: 0,
|
||||
};
|
||||
}
|
||||
|
||||
function injectAtDepthMessagesIntoChatMessages(
|
||||
chatMessages = [],
|
||||
atDepthMessages = [],
|
||||
) {
|
||||
const normalizedChatMessages = (Array.isArray(chatMessages) ? chatMessages : [])
|
||||
.map((message) => {
|
||||
const descriptor = getPromptMessageLikeDescriptor(message);
|
||||
if (!descriptor) return null;
|
||||
return {
|
||||
...(message && typeof message === "object" ? message : {}),
|
||||
role: descriptor.role,
|
||||
content: descriptor.content,
|
||||
seq: descriptor.seq,
|
||||
};
|
||||
})
|
||||
.filter(Boolean);
|
||||
if (normalizedChatMessages.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const groupedByDepth = new Map();
|
||||
for (const message of sortAtDepthInjectionEntries(atDepthMessages)) {
|
||||
const injectedMessage = createInjectedAtDepthChatMessage(message);
|
||||
if (!injectedMessage) continue;
|
||||
const depth = Math.max(0, Number(injectedMessage.depth || 0));
|
||||
if (!groupedByDepth.has(depth)) {
|
||||
groupedByDepth.set(depth, []);
|
||||
}
|
||||
groupedByDepth.get(depth).push(injectedMessage);
|
||||
}
|
||||
if (groupedByDepth.size === 0) {
|
||||
return normalizedChatMessages;
|
||||
}
|
||||
|
||||
const reversedMessages = [...normalizedChatMessages].reverse();
|
||||
const sortedDepths = [...groupedByDepth.keys()].sort((left, right) => left - right);
|
||||
let totalInsertedMessages = 0;
|
||||
|
||||
for (const depth of sortedDepths) {
|
||||
const depthMessages = groupedByDepth.get(depth) || [];
|
||||
if (depthMessages.length === 0) continue;
|
||||
const injectIndex = Math.min(
|
||||
Math.max(0, depth + totalInsertedMessages),
|
||||
reversedMessages.length,
|
||||
);
|
||||
reversedMessages.splice(injectIndex, 0, ...depthMessages);
|
||||
totalInsertedMessages += depthMessages.length;
|
||||
}
|
||||
|
||||
return reversedMessages.reverse();
|
||||
}
|
||||
|
||||
function getPromptFieldContentOrigin(sourceKey = "") {
|
||||
const normalizedSourceKey = String(sourceKey || "").trim();
|
||||
if (!normalizedSourceKey) {
|
||||
@@ -1303,6 +1434,7 @@ export async function buildTaskPrompt(settings = {}, taskType, context = {}) {
|
||||
const emptyWorldInfo = buildEmptyWorldInfoContext();
|
||||
let resolvedWorldInfo = emptyWorldInfo;
|
||||
let worldInfoRuntimeBlockedContents = [];
|
||||
let deliveredAtDepthViaChatMessages = false;
|
||||
|
||||
if (worldInfoRequested) {
|
||||
const worldInfo = await resolveTaskWorldInfo({
|
||||
@@ -1333,6 +1465,28 @@ export async function buildTaskPrompt(settings = {}, taskType, context = {}) {
|
||||
taskAdditionalMessages: sanitizedWorldInfo.additionalMessages || [],
|
||||
worldInfoDebug: sanitizedWorldInfo.debug || null,
|
||||
};
|
||||
|
||||
if (
|
||||
Array.isArray(sanitizedInputContext.chatMessages) &&
|
||||
isPromptMessageArray(sanitizedInputContext.chatMessages)
|
||||
) {
|
||||
const injectedChatMessages = injectAtDepthMessagesIntoChatMessages(
|
||||
sanitizedInputContext.chatMessages,
|
||||
sanitizedWorldInfo.additionalMessages,
|
||||
);
|
||||
if (Array.isArray(injectedChatMessages) && injectedChatMessages.length > 0) {
|
||||
sanitizedInputContext.chatMessages = injectedChatMessages;
|
||||
if (typeof context.recentMessages === "string") {
|
||||
sanitizedInputContext.recentMessages =
|
||||
stringifyInterpolatedValue(injectedChatMessages);
|
||||
}
|
||||
if (typeof context.dialogueText === "string") {
|
||||
sanitizedInputContext.dialogueText =
|
||||
stringifyInterpolatedValue(injectedChatMessages);
|
||||
}
|
||||
deliveredAtDepthViaChatMessages = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const resolvedContext = {
|
||||
@@ -1479,36 +1633,44 @@ export async function buildTaskPrompt(settings = {}, taskType, context = {}) {
|
||||
}
|
||||
}
|
||||
|
||||
const atDepthExecutionMessages = (worldInfoResolution.additionalMessages || [])
|
||||
.map((message) =>
|
||||
createExecutionMessage(
|
||||
message.role,
|
||||
message.content,
|
||||
{
|
||||
source: "worldInfo-atDepth",
|
||||
sourceKey: "taskAdditionalMessages",
|
||||
contentOrigin:
|
||||
String(message.contentOrigin || "") ||
|
||||
PROMPT_CONTENT_ORIGIN.WORLD_INFO_RENDERED,
|
||||
sanitizationEligible: message.sanitizationEligible === true,
|
||||
regexSourceType: String(message.regexSourceType || "world_info"),
|
||||
depth: Number.isFinite(Number(message?.depth))
|
||||
? Number(message.depth)
|
||||
: null,
|
||||
order: Number.isFinite(Number(message?.order))
|
||||
? Number(message.order)
|
||||
: 0,
|
||||
},
|
||||
),
|
||||
)
|
||||
.filter(Boolean);
|
||||
const finalExecutionMessages = deliveredAtDepthViaChatMessages
|
||||
? executionMessages
|
||||
: [...atDepthExecutionMessages, ...executionMessages];
|
||||
|
||||
const privateTaskMessages = deliveredAtDepthViaChatMessages
|
||||
? [...customMessages]
|
||||
: [...worldInfoResolution.additionalMessages, ...customMessages];
|
||||
debugLog(
|
||||
`[ST-BME][prompt-diag] buildTaskPrompt done: ` +
|
||||
`executionMessages=${executionMessages.length}, ` +
|
||||
`executionMessages=${finalExecutionMessages.length}, ` +
|
||||
`userBlocks=${userRoleBlockCount}, systemBlocks=${systemRoleBlockCount}, ` +
|
||||
`customMessages=${customMessages.length}`,
|
||||
`customMessages=${customMessages.length}, ` +
|
||||
`atDepthMessages=${atDepthExecutionMessages.length}, ` +
|
||||
`atDepthViaChatMessages=${deliveredAtDepthViaChatMessages}`,
|
||||
);
|
||||
|
||||
for (const message of worldInfoResolution.additionalMessages || []) {
|
||||
const executionMessage = createExecutionMessage(
|
||||
message.role,
|
||||
message.content,
|
||||
{
|
||||
source: "worldInfo-atDepth",
|
||||
sourceKey: "taskAdditionalMessages",
|
||||
contentOrigin:
|
||||
String(message.contentOrigin || "") ||
|
||||
PROMPT_CONTENT_ORIGIN.WORLD_INFO_RENDERED,
|
||||
sanitizationEligible: message.sanitizationEligible === true,
|
||||
regexSourceType: String(message.regexSourceType || "world_info"),
|
||||
},
|
||||
);
|
||||
if (executionMessage) {
|
||||
executionMessages.push(executionMessage);
|
||||
}
|
||||
}
|
||||
|
||||
const privateTaskMessages = [
|
||||
...customMessages,
|
||||
...worldInfoResolution.additionalMessages,
|
||||
];
|
||||
const hostInjectionPlan = buildHostInjectionPlan(
|
||||
renderedBlocks,
|
||||
worldInfoResolution,
|
||||
@@ -1522,7 +1684,7 @@ export async function buildTaskPrompt(settings = {}, taskType, context = {}) {
|
||||
systemPrompt,
|
||||
messages: privateTaskMessages,
|
||||
},
|
||||
executionMessages,
|
||||
executionMessages: finalExecutionMessages,
|
||||
privateTaskMessages,
|
||||
renderedBlocks,
|
||||
regexInput: mergeRegexCollectors(promptRegexInput),
|
||||
@@ -1562,14 +1724,16 @@ export async function buildTaskPrompt(settings = {}, taskType, context = {}) {
|
||||
customMessageCount: customMessages.length,
|
||||
additionalMessageCount: worldInfoResolution.additionalMessages.length,
|
||||
privateTaskMessageCount: privateTaskMessages.length,
|
||||
executionMessageCount: executionMessages.length,
|
||||
executionMessageCount: finalExecutionMessages.length,
|
||||
userRoleBlockCount,
|
||||
assistantRoleBlockCount,
|
||||
systemRoleBlockCount,
|
||||
effectiveDelivery: {
|
||||
profileBlocks: "ordered-private-messages",
|
||||
worldInfoBeforeAfter: "inline-in-ordered-messages",
|
||||
worldInfoAtDepth: "appended-private-messages",
|
||||
worldInfoAtDepth: deliveredAtDepthViaChatMessages
|
||||
? "inserted-into-chat-messages-by-depth"
|
||||
: "appended-private-messages-fallback",
|
||||
},
|
||||
worldInfoCacheHit: Boolean(worldInfoResolution.debug?.cache?.hit),
|
||||
ejsRuntimeStatus: worldInfoResolution.debug?.ejsRuntimeStatus || "",
|
||||
@@ -1639,7 +1803,7 @@ export async function buildTaskPrompt(settings = {}, taskType, context = {}) {
|
||||
profileName: profile?.name || "",
|
||||
systemPrompt,
|
||||
privateTaskMessages,
|
||||
executionMessages,
|
||||
executionMessages: finalExecutionMessages,
|
||||
renderedBlocks,
|
||||
hostInjections: worldInfoResolution.injections,
|
||||
hostInjectionPlan,
|
||||
|
||||
@@ -1158,6 +1158,7 @@ function normalizeResolvedEntry(entry = {}, fallbackIndex = 0) {
|
||||
? entry.role
|
||||
: "system";
|
||||
return {
|
||||
uid: Number(entry.uid ?? 0),
|
||||
name: normalizeKey(entry.name),
|
||||
sourceName: normalizeKey(
|
||||
entry.sourceName || entry.source_name || entry.name,
|
||||
@@ -1185,6 +1186,7 @@ function sortAtDepthEntries(entries = []) {
|
||||
return (
|
||||
depthB - depthA ||
|
||||
(a.order ?? 100) - (b.order ?? 100) ||
|
||||
(b.uid ?? 0) - (a.uid ?? 0) ||
|
||||
a.index - b.index
|
||||
);
|
||||
});
|
||||
@@ -1195,6 +1197,13 @@ function buildAdditionalMessages(entries = []) {
|
||||
.map((entry) => ({
|
||||
role: entry.role,
|
||||
content: String(entry.content || "").trim(),
|
||||
depth: Number(entry.depth ?? 0),
|
||||
order: Number(entry.order ?? 100),
|
||||
uid: Number(entry.uid ?? 0),
|
||||
index: Number(entry.index ?? 0),
|
||||
name: String(entry.name || ""),
|
||||
sourceName: String(entry.sourceName || entry.name || ""),
|
||||
worldbook: String(entry.worldbook || ""),
|
||||
source: "worldInfo-atDepth",
|
||||
sourceKey: "taskAdditionalMessages",
|
||||
}))
|
||||
|
||||
@@ -265,7 +265,7 @@ const bonusEntry = createWorldbookEntry({
|
||||
order: 10,
|
||||
});
|
||||
|
||||
const bonusMvuEntry = createWorldbookEntry({
|
||||
const bonusMvuEntry = createWorldbookEntry({
|
||||
uid: 102,
|
||||
name: "Bonus MVU",
|
||||
comment: "Bonus MVU",
|
||||
@@ -273,8 +273,8 @@ const bonusMvuEntry = createWorldbookEntry({
|
||||
order: 20,
|
||||
});
|
||||
|
||||
const worldbooksByName = {
|
||||
"main-book": [
|
||||
const worldbooksByName = {
|
||||
"main-book": [
|
||||
constantEntry,
|
||||
dynEntry,
|
||||
inlineSummaryEntry,
|
||||
@@ -293,9 +293,9 @@ const worldbooksByName = {
|
||||
atDepthEntry,
|
||||
mvuTaggedEntry,
|
||||
mvuHeuristicEntry,
|
||||
],
|
||||
"bonus-book": [bonusEntry, bonusMvuEntry],
|
||||
};
|
||||
],
|
||||
"bonus-book": [bonusEntry, bonusMvuEntry],
|
||||
};
|
||||
|
||||
try {
|
||||
globalThis.SillyTavern = {
|
||||
@@ -663,7 +663,11 @@ try {
|
||||
);
|
||||
assert.deepEqual(
|
||||
promptBuild.privateTaskMessages.map((message) => message.role),
|
||||
["user", "system"],
|
||||
["system", "user"],
|
||||
);
|
||||
assert.equal(
|
||||
promptBuild.privateTaskMessages[0].content,
|
||||
"这是一条 atDepth 消息。",
|
||||
);
|
||||
assert.deepEqual(
|
||||
promptBuild.hostInjections.before.map((entry) => entry.name),
|
||||
@@ -703,7 +707,11 @@ try {
|
||||
assert.equal(promptBuild.executionMessages.length, 4);
|
||||
assert.deepEqual(
|
||||
promptBuild.executionMessages.map((message) => message.role),
|
||||
["system", "system", "user", "system"],
|
||||
["system", "system", "system", "user"],
|
||||
);
|
||||
assert.equal(
|
||||
promptBuild.executionMessages[0].content,
|
||||
"这是一条 atDepth 消息。",
|
||||
);
|
||||
assert.deepEqual(
|
||||
promptBuild.renderedBlocks.map((block) => block.delivery),
|
||||
@@ -845,8 +853,106 @@ try {
|
||||
);
|
||||
assert.deepEqual(
|
||||
atDepthOnlyPromptBuild.executionMessages.map((message) => message.role),
|
||||
["user", "system"],
|
||||
["system", "user"],
|
||||
);
|
||||
assert.equal(
|
||||
atDepthOnlyPromptBuild.executionMessages[0].content,
|
||||
"这是一条 atDepth 消息。",
|
||||
);
|
||||
|
||||
const depthD4Entry = createWorldbookEntry({
|
||||
uid: 201,
|
||||
name: "深度注入 D4",
|
||||
comment: "深度注入 D4",
|
||||
content: "这是 d4 atDepth 消息。",
|
||||
positionType: "at_depth_as_system",
|
||||
depth: 4,
|
||||
order: 8,
|
||||
});
|
||||
const depthD1Entry = createWorldbookEntry({
|
||||
uid: 202,
|
||||
name: "深度注入 D1",
|
||||
comment: "深度注入 D1",
|
||||
content: "这是 d1 atDepth 消息。",
|
||||
positionType: "at_depth_as_system",
|
||||
depth: 1,
|
||||
order: 3,
|
||||
});
|
||||
worldbooksByName["main-book"].push(depthD4Entry, depthD1Entry);
|
||||
const previousGetContext = globalThis.SillyTavern.getContext;
|
||||
globalThis.SillyTavern.getContext = () => ({
|
||||
...previousGetContext(),
|
||||
chatId: "depth-aware-chat",
|
||||
});
|
||||
|
||||
const depthAwareSettings = {
|
||||
taskProfiles: {
|
||||
recall: {
|
||||
activeProfileId: "depth-aware",
|
||||
profiles: [
|
||||
{
|
||||
id: "depth-aware",
|
||||
name: "深度顺序预设",
|
||||
taskType: "recall",
|
||||
builtin: false,
|
||||
blocks: [
|
||||
{
|
||||
id: "depth-recent",
|
||||
type: "builtin",
|
||||
sourceKey: "recentMessages",
|
||||
role: "system",
|
||||
enabled: true,
|
||||
order: 0,
|
||||
injectionMode: "append",
|
||||
},
|
||||
{
|
||||
id: "depth-user",
|
||||
type: "custom",
|
||||
content: "用户问题:{{userMessage}}",
|
||||
role: "user",
|
||||
enabled: true,
|
||||
order: 1,
|
||||
injectionMode: "append",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const depthAwarePromptBuild = await buildTaskPrompt(depthAwareSettings, "recall", {
|
||||
taskName: "recall",
|
||||
userMessage: "继续调查 depth 排序",
|
||||
recentMessages: "这里会被 chatMessages 替换",
|
||||
chatMessages: [
|
||||
{ seq: 11, role: "user", content: "第一句" },
|
||||
{ seq: 12, role: "assistant", content: "第二句" },
|
||||
],
|
||||
charName: "Alice",
|
||||
});
|
||||
|
||||
assert.deepEqual(
|
||||
depthAwarePromptBuild.executionMessages.map((message) => message.content),
|
||||
[
|
||||
"#1 [assistant]: 这是 d4 atDepth 消息。\n\n#2 [assistant]: 这是一条 atDepth 消息。\n\n#11 [user]: 第一句\n\n#4 [assistant]: 这是 d1 atDepth 消息。\n\n#12 [assistant]: 第二句",
|
||||
"用户问题:继续调查 depth 排序",
|
||||
],
|
||||
);
|
||||
assert.deepEqual(
|
||||
depthAwarePromptBuild.hostInjections.atDepth.map((entry) => entry.name),
|
||||
["深度注入 D4", "深度注入", "深度注入 D1"],
|
||||
);
|
||||
assert.deepEqual(
|
||||
depthAwarePromptBuild.hostInjectionPlan.atDepth.map((entry) => entry.entryName),
|
||||
["深度注入 D4", "深度注入", "深度注入 D1"],
|
||||
);
|
||||
assert.equal(
|
||||
depthAwarePromptBuild.executionMessages.at(-1)?.content.includes("atDepth"),
|
||||
false,
|
||||
);
|
||||
worldbooksByName["main-book"].splice(-2, 2);
|
||||
globalThis.SillyTavern.getContext = previousGetContext;
|
||||
|
||||
const { initializeHostAdapter } = await import("../host/adapter/index.js");
|
||||
const partialBridgeCalls = [];
|
||||
|
||||
Reference in New Issue
Block a user