refactor: controllerize onReroll and keep reroll harness green

This commit is contained in:
Youzini-afk
2026-03-29 17:39:41 +08:00
parent b7c986d9ea
commit 5023b75f3f
3 changed files with 196 additions and 159 deletions

180
index.js
View File

@@ -21,6 +21,7 @@ import { consolidateMemories } from "./consolidator.js";
import {
executeExtractionBatchController,
onManualExtractController,
onRerollController,
runExtractionController,
} from "./extraction-controller.js";
import {
@@ -4642,166 +4643,27 @@ async function onManualExtract() {
}
async function onReroll({ fromFloor } = {}) {
if (isExtracting) {
toastr.info("记忆提取正在进行中,请稍候");
return {
success: false,
rollbackPerformed: false,
extractionTriggered: false,
requestedFloor: null,
effectiveFromFloor: null,
recoveryPath: "busy",
affectedBatchCount: 0,
error: "记忆提取正在进行中",
};
}
if (
typeof ensureGraphMutationReady === "function" &&
!ensureGraphMutationReady("重新提取")
) {
return {
success: false,
rollbackPerformed: false,
extractionTriggered: false,
requestedFloor: Number.isFinite(fromFloor) ? fromFloor : null,
effectiveFromFloor: null,
recoveryPath:
typeof graphPersistenceState === "object"
? graphPersistenceState.loadState
: "graph-not-ready",
affectedBatchCount: 0,
error:
typeof getGraphMutationBlockReason === "function"
? getGraphMutationBlockReason("重新提取")
: "重新提取已暂停:图谱尚未就绪。",
};
}
if (!currentGraph) {
toastr.info("图谱为空,无需重 Roll");
return {
success: false,
rollbackPerformed: false,
extractionTriggered: false,
requestedFloor: null,
effectiveFromFloor: null,
recoveryPath: "empty-graph",
affectedBatchCount: 0,
error: "图谱为空",
};
}
const context = getContext();
const chat = context.chat;
if (!Array.isArray(chat) || chat.length === 0) {
toastr.info("当前聊天为空");
return {
success: false,
rollbackPerformed: false,
extractionTriggered: false,
requestedFloor: null,
effectiveFromFloor: null,
recoveryPath: "empty-chat",
affectedBatchCount: 0,
error: "当前聊天为空",
};
}
// 确定回滚起点
let targetFloor = Number.isFinite(fromFloor) ? fromFloor : null;
if (targetFloor === null) {
// 默认:重做最新 AI 楼
const assistantTurns = getAssistantTurns(chat);
if (assistantTurns.length === 0) {
toastr.info("聊天中没有 AI 回复");
return {
success: false,
rollbackPerformed: false,
extractionTriggered: false,
requestedFloor: null,
effectiveFromFloor: null,
recoveryPath: "no-assistant-turn",
affectedBatchCount: 0,
error: "聊天中没有 AI 回复",
};
}
targetFloor = assistantTurns[assistantTurns.length - 1];
}
setRuntimeStatus(
"重新提取中",
Number.isFinite(targetFloor)
? `准备从楼层 ${targetFloor} 开始回滚并重新提取`
: "准备回滚最新 AI 楼并重新提取",
"running",
return await onRerollController(
{
console,
ensureGraphMutationReady,
getAssistantTurns,
getContext,
getCurrentGraph: () => currentGraph,
getGraphMutationBlockReason,
getGraphPersistenceState: () => graphPersistenceState,
getIsExtracting: () => isExtracting,
getLastExtractionStatusLevel: () => lastExtractionStatus?.level || "idle",
getLastProcessedAssistantFloor,
isAbortError,
onManualExtract,
refreshPanelLiveState,
rollbackGraphForReroll,
setRuntimeStatus,
toastr,
},
{ fromFloor },
);
const lastProcessed = getLastProcessedAssistantFloor();
const alreadyExtracted = targetFloor <= lastProcessed;
if (!alreadyExtracted) {
// 目标楼层未提取过 → 直接走手动提取即可,不需要回滚
toastr.info("该楼层尚未提取,直接执行提取…", "ST-BME 重 Roll", {
timeOut: 2000,
});
await onManualExtract();
return {
success: true,
rollbackPerformed: false,
extractionTriggered: true,
requestedFloor: targetFloor,
effectiveFromFloor: lastProcessed + 1,
recoveryPath: "direct-extract",
affectedBatchCount: 0,
extractionStatus: lastExtractionStatus?.level || "idle",
error: "",
};
}
console.log(`[ST-BME] 重 Roll 开始,目标楼层: ${targetFloor}`);
let rollbackResult;
try {
rollbackResult = await rollbackGraphForReroll(targetFloor, context);
} catch (e) {
if (isAbortError(e)) {
setRuntimeStatus('重新提取已取消', e.message || '聊天已切换', 'warning');
return {
success: false,
rollbackPerformed: false,
extractionTriggered: false,
requestedFloor: targetFloor,
effectiveFromFloor: null,
recoveryPath: 'aborted',
affectedBatchCount: 0,
error: e.message || '聊天已切换,重新提取已取消',
};
}
throw e;
}
if (!rollbackResult?.success) {
setRuntimeStatus(
"重新提取失败",
rollbackResult.error || "回滚失败",
"error",
);
toastr.error(rollbackResult.error, "ST-BME 重 Roll");
return rollbackResult;
}
const rerollDesc =
rollbackResult.effectiveFromFloor !== targetFloor
? `已按批次边界回滚到楼层 ${rollbackResult.effectiveFromFloor} 开始重新提取…`
: `已回滚到楼层 ${targetFloor} 开始重新提取…`;
toastr.info(rerollDesc, "ST-BME 重 Roll", {
timeOut: 2500,
});
await onManualExtract();
refreshPanelLiveState();
return {
...rollbackResult,
extractionTriggered: true,
extractionStatus: lastExtractionStatus?.level || "idle",
};
}
async function onManualSleep() {