mirror of
https://github.com/Youzini-afk/ST-Bionic-Memory-Ecology.git
synced 2026-05-15 14:20:35 +08:00
fix(recall): reuse user-floor cache without target index
This commit is contained in:
@@ -94,30 +94,20 @@ function buildPersistedRecallReuseResult(record = {}) {
|
||||
function resolveReusablePersistedRecallRecord(chat, recallInput, runtime) {
|
||||
const generationType = String(recallInput?.generationType || "normal").trim() || "normal";
|
||||
|
||||
const targetUserMessageIndex = Number.isFinite(recallInput?.targetUserMessageIndex)
|
||||
let targetUserMessageIndex = Number.isFinite(recallInput?.targetUserMessageIndex)
|
||||
? Math.floor(Number(recallInput.targetUserMessageIndex))
|
||||
: null;
|
||||
if (!Number.isFinite(targetUserMessageIndex)) return null;
|
||||
|
||||
const targetMessage = Array.isArray(chat) ? chat[targetUserMessageIndex] : null;
|
||||
if (!targetMessage?.is_user) return null;
|
||||
|
||||
const readPersistedRecallFromUserMessage = runtime.readPersistedRecallFromUserMessage;
|
||||
if (typeof readPersistedRecallFromUserMessage !== "function") return null;
|
||||
|
||||
const record = readPersistedRecallFromUserMessage(chat, targetUserMessageIndex);
|
||||
if (!record?.injectionText) return null;
|
||||
|
||||
const normalizeText = (value = "") =>
|
||||
typeof runtime.normalizeRecallInputText === "function"
|
||||
? runtime.normalizeRecallInputText(value)
|
||||
: String(value ?? "")
|
||||
.replace(/\r\n/g, "\n")
|
||||
.trim();
|
||||
const currentUserFloorText = normalizeText(targetMessage?.mes || "");
|
||||
const currentRecallInputText = normalizeText(recallInput?.userMessage || "");
|
||||
const recordRecallInput = normalizeText(record?.recallInput || "");
|
||||
const boundUserFloorText = normalizeText(record?.boundUserFloorText || "");
|
||||
const recallSource = String(recallInput?.source || "").trim();
|
||||
const activeInputSources = new Set([
|
||||
"send-intent",
|
||||
@@ -129,6 +119,50 @@ function resolveReusablePersistedRecallRecord(chat, recallInput, runtime) {
|
||||
]);
|
||||
const isActiveInputSource = activeInputSources.has(recallSource);
|
||||
|
||||
if (!Number.isFinite(targetUserMessageIndex)) {
|
||||
if (!currentRecallInputText || isActiveInputSource || !Array.isArray(chat)) {
|
||||
return null;
|
||||
}
|
||||
for (let index = chat.length - 1; index >= 0; index--) {
|
||||
const message = chat[index];
|
||||
if (!message?.is_user) continue;
|
||||
const candidateRecord = readPersistedRecallFromUserMessage(chat, index);
|
||||
if (!candidateRecord?.injectionText) continue;
|
||||
const candidateUserFloorText = normalizeText(message?.mes || "");
|
||||
const candidateBoundUserFloorText = normalizeText(
|
||||
candidateRecord?.boundUserFloorText || "",
|
||||
);
|
||||
if (
|
||||
candidateBoundUserFloorText &&
|
||||
candidateUserFloorText !== candidateBoundUserFloorText
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
const candidateRecallInput = normalizeText(candidateRecord?.recallInput || "");
|
||||
if (
|
||||
currentRecallInputText === candidateUserFloorText ||
|
||||
(candidateBoundUserFloorText &&
|
||||
currentRecallInputText === candidateBoundUserFloorText) ||
|
||||
(candidateRecallInput && currentRecallInputText === candidateRecallInput)
|
||||
) {
|
||||
targetUserMessageIndex = index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!Number.isFinite(targetUserMessageIndex)) return null;
|
||||
|
||||
const targetMessage = Array.isArray(chat) ? chat[targetUserMessageIndex] : null;
|
||||
if (!targetMessage?.is_user) return null;
|
||||
|
||||
const record = readPersistedRecallFromUserMessage(chat, targetUserMessageIndex);
|
||||
if (!record?.injectionText) return null;
|
||||
|
||||
const currentUserFloorText = normalizeText(targetMessage?.mes || "");
|
||||
const recordRecallInput = normalizeText(record?.recallInput || "");
|
||||
const boundUserFloorText = normalizeText(record?.boundUserFloorText || "");
|
||||
|
||||
const matchesBoundUserFloor = Boolean(
|
||||
currentUserFloorText &&
|
||||
boundUserFloorText &&
|
||||
|
||||
@@ -708,4 +708,73 @@ assert.equal(
|
||||
console.log(" ✓ runRecallController reuses user-floor record with empty recallInput");
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// 5. runRecallController: normal generation below an assistant reuses user-floor record
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
const assistantTailChat = [
|
||||
{ is_user: true, mes: "今晚去海边看烟花" },
|
||||
{ is_user: false, mes: "好,我会准备好相机。", is_system: false },
|
||||
];
|
||||
const assistantTailRecord = buildPersistedRecallRecord({
|
||||
injectionText: "注入:今晚去海边看烟花",
|
||||
selectedNodeIds: ["node-fireworks"],
|
||||
recallInput: "今晚去海边看烟花",
|
||||
recallSource: "chat-latest-user",
|
||||
hookName: "GENERATION_AFTER_COMMANDS",
|
||||
tokenEstimate: 4,
|
||||
manuallyEdited: false,
|
||||
boundUserFloorText: "今晚去海边看烟花",
|
||||
});
|
||||
writePersistedRecallToUserMessage(assistantTailChat, 0, assistantTailRecord);
|
||||
|
||||
let assistantTailRetrieveCalled = false;
|
||||
const assistantTailRuntime = {
|
||||
...rerollRuntime,
|
||||
getContext: () => ({ chat: assistantTailChat, chatId: "chat-assistant-tail" }),
|
||||
readPersistedRecallFromUserMessage,
|
||||
retrieve: async () => {
|
||||
assistantTailRetrieveCalled = true;
|
||||
return {
|
||||
injectionText: "fresh recall should not run",
|
||||
selectedNodeIds: ["node-fresh"],
|
||||
};
|
||||
},
|
||||
resolveRecallInput: (chat, limit, override) => ({
|
||||
userMessage: normalizeRecallInputText(
|
||||
override?.overrideUserMessage || override?.userMessage || "今晚去海边看烟花",
|
||||
),
|
||||
generationType: String(override?.generationType || "normal"),
|
||||
targetUserMessageIndex: Number.isFinite(override?.targetUserMessageIndex)
|
||||
? override.targetUserMessageIndex
|
||||
: null,
|
||||
source: override?.overrideSource || "chat-latest-user",
|
||||
sourceLabel: override?.overrideSourceLabel || "最近用户消息",
|
||||
reason: "assistant-tail-normal-generation",
|
||||
authoritativeInputUsed: Boolean(override?.authoritativeInputUsed),
|
||||
boundUserFloorText: normalizeRecallInputText(
|
||||
override?.boundUserFloorText || "今晚去海边看烟花",
|
||||
),
|
||||
recentMessages: [],
|
||||
hookName: override?.hookName || "",
|
||||
deliveryMode: "immediate",
|
||||
}),
|
||||
};
|
||||
|
||||
const assistantTailResult = await runRecallController(assistantTailRuntime, {
|
||||
overrideUserMessage: "今晚去海边看烟花",
|
||||
generationType: "normal",
|
||||
overrideSource: "chat-latest-user",
|
||||
hookName: "GENERATION_AFTER_COMMANDS",
|
||||
deliveryMode: "immediate",
|
||||
});
|
||||
|
||||
assert.equal(assistantTailResult.status, "completed");
|
||||
assert.equal(
|
||||
assistantTailRetrieveCalled,
|
||||
false,
|
||||
"normal generation below an assistant should find and reuse the matching user-floor persisted recall",
|
||||
);
|
||||
assert.equal(assistantTailResult.reason, "persisted-user-floor-reused");
|
||||
|
||||
console.log(" ✓ runRecallController reuses user-floor record below assistant tail");
|
||||
console.log("recall-reroll-reuse tests passed");
|
||||
|
||||
Reference in New Issue
Block a user