mirror of
https://github.com/Youzini-afk/ST-Bionic-Memory-Ecology.git
synced 2026-05-15 22:30:38 +08:00
feat: 重Roll系统-回滚journal+重提取
This commit is contained in:
66
index.js
66
index.js
@@ -3740,6 +3740,71 @@ async function onManualExtract() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function onReroll({ fromFloor } = {}) {
|
||||||
|
if (isExtracting) {
|
||||||
|
toastr.info("记忆提取正在进行中,请稍候");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!currentGraph) {
|
||||||
|
toastr.info("图谱为空,无需重 Roll");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const context = getContext();
|
||||||
|
const chat = context.chat;
|
||||||
|
if (!Array.isArray(chat) || chat.length === 0) {
|
||||||
|
toastr.info("当前聊天为空");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确定回滚起点
|
||||||
|
let targetFloor = Number.isFinite(fromFloor) ? fromFloor : null;
|
||||||
|
if (targetFloor === null) {
|
||||||
|
// 默认:回滚到最新 AI 楼
|
||||||
|
targetFloor = getLastProcessedAssistantFloor();
|
||||||
|
if (targetFloor < 0) {
|
||||||
|
toastr.info("尚未有过提取记录,无需重 Roll");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`[ST-BME] 重 Roll 开始,目标楼层: ${targetFloor}`);
|
||||||
|
|
||||||
|
// 1. 找到受影响的 journal 并回滚
|
||||||
|
const recovery = findJournalRecoveryPoint(currentGraph, targetFloor);
|
||||||
|
if (recovery && recovery.affectedJournals?.length > 0) {
|
||||||
|
rollbackAffectedJournals(currentGraph, recovery.affectedJournals);
|
||||||
|
console.log(
|
||||||
|
`[ST-BME] 已回滚 ${recovery.affectedJournals.length} 个 batch`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 重置提取指针
|
||||||
|
const newFloor = targetFloor - 1;
|
||||||
|
currentGraph.historyState.lastProcessedAssistantFloor = newFloor;
|
||||||
|
currentGraph.lastProcessedSeq = newFloor;
|
||||||
|
|
||||||
|
// 3. 清理 processedMessageHashes 中 >= targetFloor 的条目
|
||||||
|
const hashes = currentGraph.historyState.processedMessageHashes || {};
|
||||||
|
for (const key of Object.keys(hashes)) {
|
||||||
|
if (Number(key) >= targetFloor) {
|
||||||
|
delete hashes[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 保存回滚后的状态
|
||||||
|
saveGraph();
|
||||||
|
|
||||||
|
toastr.info(
|
||||||
|
`已回滚到楼层 ${targetFloor} 之前,开始重新提取…`,
|
||||||
|
"ST-BME 重 Roll",
|
||||||
|
{ timeOut: 2000 },
|
||||||
|
);
|
||||||
|
|
||||||
|
// 5. 触发重新提取(复用手动提取逻辑)
|
||||||
|
await onManualExtract();
|
||||||
|
}
|
||||||
|
|
||||||
async function onManualSleep() {
|
async function onManualSleep() {
|
||||||
if (!currentGraph) return;
|
if (!currentGraph) return;
|
||||||
const beforeSnapshot = cloneGraphSnapshot(currentGraph);
|
const beforeSnapshot = cloneGraphSnapshot(currentGraph);
|
||||||
@@ -3917,6 +3982,7 @@ async function onReembedDirect() {
|
|||||||
rebuildVectorIndex: () => onRebuildVectorIndex(),
|
rebuildVectorIndex: () => onRebuildVectorIndex(),
|
||||||
rebuildVectorRange: (range) => onRebuildVectorIndex(range),
|
rebuildVectorRange: (range) => onRebuildVectorIndex(range),
|
||||||
reembedDirect: onReembedDirect,
|
reembedDirect: onReembedDirect,
|
||||||
|
reroll: onReroll,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
20
panel.html
20
panel.html
@@ -255,6 +255,26 @@
|
|||||||
<i class="fa-solid fa-moon"></i>
|
<i class="fa-solid fa-moon"></i>
|
||||||
<span>执行遗忘</span>
|
<span>执行遗忘</span>
|
||||||
</button>
|
</button>
|
||||||
|
<button class="bme-action-btn" id="bme-act-reroll" type="button">
|
||||||
|
<i class="fa-solid fa-rotate"></i>
|
||||||
|
<span>重新提取</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="bme-action-group-extra">
|
||||||
|
<div class="bme-config-help">
|
||||||
|
重新提取:回滚指定楼层及之后的提取结果并重做。留空则只重做最新 AI 楼。
|
||||||
|
</div>
|
||||||
|
<div class="bme-config-row">
|
||||||
|
<label for="bme-reroll-floor">起始楼层</label>
|
||||||
|
<input
|
||||||
|
id="bme-reroll-floor"
|
||||||
|
class="bme-config-input"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
max="999999"
|
||||||
|
placeholder="留空 = 最新 AI 楼"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
46
panel.js
46
panel.js
@@ -828,6 +828,52 @@ function _bindActions() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 重新提取 (reroll) 绑定
|
||||||
|
document
|
||||||
|
.getElementById("bme-act-reroll")
|
||||||
|
?.addEventListener("click", async () => {
|
||||||
|
const btn = document.getElementById("bme-act-reroll");
|
||||||
|
if (btn?.disabled) return;
|
||||||
|
|
||||||
|
const floorStr = document.getElementById("bme-reroll-floor")?.value;
|
||||||
|
const fromFloor = _parseOptionalInt(floorStr);
|
||||||
|
const desc = Number.isFinite(fromFloor)
|
||||||
|
? `从楼层 ${fromFloor} 开始回滚并重新提取`
|
||||||
|
: "回滚最新 AI 楼并重新提取";
|
||||||
|
|
||||||
|
if (!confirm(`确认要重新提取吗?\n\n${desc}\n\n已提取的记忆节点将被回滚。`)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (btn) {
|
||||||
|
btn.disabled = true;
|
||||||
|
btn.style.opacity = "0.5";
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await _actionHandlers.reroll?.({
|
||||||
|
fromFloor: Number.isFinite(fromFloor) ? fromFloor : undefined,
|
||||||
|
});
|
||||||
|
_refreshDashboard();
|
||||||
|
_refreshGraph();
|
||||||
|
if (
|
||||||
|
document
|
||||||
|
.getElementById("bme-pane-memory")
|
||||||
|
?.classList.contains("active")
|
||||||
|
) {
|
||||||
|
_refreshMemoryBrowser();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("[ST-BME] Action reroll failed:", error);
|
||||||
|
toastr.error(`重新提取失败: ${error?.message || error}`, "ST-BME");
|
||||||
|
} finally {
|
||||||
|
if (btn) {
|
||||||
|
btn.disabled = false;
|
||||||
|
btn.style.opacity = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function _refreshConfigTab() {
|
function _refreshConfigTab() {
|
||||||
|
|||||||
Reference in New Issue
Block a user