mirror of
https://github.com/Youzini-afk/ST-Bionic-Memory-Ecology.git
synced 2026-06-14 02:40:45 +08:00
feat(recall): resolve reroll parent user floor
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
// ST-BME: 持久化召回记录纯函数
|
||||
|
||||
import { resolveGenerationParentUserFloor } from "../runtime/generation-context.js";
|
||||
|
||||
export const BME_RECALL_EXTRA_KEY = "bme_recall";
|
||||
export const BME_RECALL_VERSION = 1;
|
||||
|
||||
@@ -135,25 +137,7 @@ export function resolveGenerationTargetUserMessageIndex(
|
||||
{ generationType = "normal" } = {},
|
||||
) {
|
||||
if (!Array.isArray(chat) || chat.length === 0) return null;
|
||||
|
||||
const normalizedType = String(generationType || "normal").trim() || "normal";
|
||||
|
||||
// normal:取「最后一条非系统用户楼层」。若直接 return 末条非 user(常见为刚追加的助手回合),
|
||||
// 会得到 null,导致持久化无法回绑到本轮 user,`hasRecordForLatest` 长期为 false。
|
||||
if (normalizedType === "normal") {
|
||||
for (let index = chat.length - 1; index >= 0; index--) {
|
||||
const message = chat[index];
|
||||
if (message?.is_system) continue;
|
||||
if (message?.is_user) return index;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
for (let index = chat.length - 1; index >= 0; index--) {
|
||||
if (chat[index]?.is_user) return index;
|
||||
}
|
||||
|
||||
return null;
|
||||
return resolveGenerationParentUserFloor(chat, { type: generationType });
|
||||
}
|
||||
|
||||
export function resolveFinalRecallInjectionSource({
|
||||
|
||||
@@ -23,6 +23,66 @@ export function classifyGenerationKind(type = "normal", params = {}) {
|
||||
return "fresh";
|
||||
}
|
||||
|
||||
export function isVisibleUserGenerationMessage(message, { index = null, chat = null, isSystemMessage = null } = {}) {
|
||||
if (!message?.is_user) return false;
|
||||
if (message?.extra?.isSmallSys) return false;
|
||||
if (typeof isSystemMessage === "function" && isSystemMessage(message, { index, chat })) {
|
||||
return false;
|
||||
}
|
||||
if (message?.is_system) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
export function resolveGenerationParentUserFloor(
|
||||
chat,
|
||||
context = {},
|
||||
{ phase = "", isSystemMessage = null } = {},
|
||||
) {
|
||||
if (!Array.isArray(chat) || chat.length === 0) return null;
|
||||
const generationType = normalizeGenerationType(context?.type || context?.generationType || "normal");
|
||||
const findVisibleUserBefore = (startIndex) => {
|
||||
for (let index = Math.min(chat.length - 1, Math.floor(Number(startIndex))); index >= 0; index--) {
|
||||
if (isVisibleUserGenerationMessage(chat[index], { index, chat, isSystemMessage })) return index;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
const findLastVisibleNonSystemIndex = () => {
|
||||
for (let index = chat.length - 1; index >= 0; index--) {
|
||||
const message = chat[index];
|
||||
if (!message) continue;
|
||||
if (message?.extra?.isSmallSys) continue;
|
||||
if (typeof isSystemMessage === "function" && isSystemMessage(message, { index, chat })) continue;
|
||||
if (message?.is_system) continue;
|
||||
return index;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
if (generationType === "swipe") {
|
||||
const swipedFloor = Number(context?.swipedAssistantFloor);
|
||||
if (Number.isFinite(swipedFloor)) return findVisibleUserBefore(swipedFloor - 1);
|
||||
const lastVisible = findLastVisibleNonSystemIndex();
|
||||
return Number.isFinite(lastVisible) ? findVisibleUserBefore(lastVisible - 1) : null;
|
||||
}
|
||||
|
||||
if (generationType === "regenerate") {
|
||||
const lastVisible = findLastVisibleNonSystemIndex();
|
||||
if (!Number.isFinite(lastVisible)) return null;
|
||||
if (isVisibleUserGenerationMessage(chat[lastVisible], { index: lastVisible, chat, isSystemMessage })) {
|
||||
return lastVisible;
|
||||
}
|
||||
return findVisibleUserBefore(lastVisible - 1);
|
||||
}
|
||||
|
||||
if (generationType === "continue") {
|
||||
const lastVisible = findLastVisibleNonSystemIndex();
|
||||
if (!Number.isFinite(lastVisible)) return null;
|
||||
return findVisibleUserBefore(lastVisible - (chat[lastVisible]?.is_user ? 0 : 1));
|
||||
}
|
||||
|
||||
return findVisibleUserBefore(chat.length - 1);
|
||||
}
|
||||
|
||||
function clonePlain(value, fallback = null) {
|
||||
if (!value || typeof value !== "object") return fallback;
|
||||
try {
|
||||
|
||||
@@ -2,6 +2,7 @@ import assert from "node:assert/strict";
|
||||
import {
|
||||
classifyGenerationKind,
|
||||
createGenerationContextTracker,
|
||||
resolveGenerationParentUserFloor,
|
||||
} from "../runtime/generation-context.js";
|
||||
|
||||
assert.equal(classifyGenerationKind("normal"), "fresh");
|
||||
@@ -100,3 +101,48 @@ assert.equal(classifyGenerationKind("normal", { quiet_prompt: true }), "skip");
|
||||
chatId = "chat-original";
|
||||
assert.equal(tracker.get(), null);
|
||||
}
|
||||
|
||||
{
|
||||
const chat = [
|
||||
{ is_system: true, mes: "greeting" },
|
||||
{ is_user: true, mes: "first" },
|
||||
{ is_user: false, mes: "assistant first" },
|
||||
{ is_user: true, mes: "parent" },
|
||||
{ is_user: false, mes: "assistant active" },
|
||||
];
|
||||
|
||||
assert.equal(
|
||||
resolveGenerationParentUserFloor(chat, {
|
||||
type: "swipe",
|
||||
swipedAssistantFloor: 4,
|
||||
}),
|
||||
3,
|
||||
);
|
||||
assert.equal(resolveGenerationParentUserFloor(chat, { type: "regenerate" }), 3);
|
||||
}
|
||||
|
||||
{
|
||||
const chatAfterRegenerateDelete = [
|
||||
{ is_system: true, mes: "greeting" },
|
||||
{ is_user: true, mes: "parent" },
|
||||
];
|
||||
assert.equal(
|
||||
resolveGenerationParentUserFloor(chatAfterRegenerateDelete, {
|
||||
type: "regenerate",
|
||||
}),
|
||||
1,
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
const chat = [
|
||||
{ is_system: true, mes: "greeting" },
|
||||
{ is_user: true, mes: "hidden", is_system: true },
|
||||
{ is_user: true, mes: "visible" },
|
||||
{ is_user: false, mes: "assistant" },
|
||||
];
|
||||
assert.equal(
|
||||
resolveGenerationParentUserFloor(chat, { type: "swipe", swipedAssistantFloor: 3 }),
|
||||
2,
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user