feat(recall): resolve reroll parent user floor

This commit is contained in:
youzini
2026-05-31 20:14:32 +00:00
parent 01291acb2d
commit 5b5930e442
3 changed files with 109 additions and 19 deletions

View File

@@ -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({

View File

@@ -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 {

View File

@@ -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,
);
}