From 18897dc6bf6e25cda4e6915ebe9ec160ac124fb8 Mon Sep 17 00:00:00 2001 From: Youzini-afk <13153778771cx@gmail.com> Date: Sun, 29 Mar 2026 17:16:30 +0800 Subject: [PATCH] refactor: move manual rebuild flow into ui actions controller --- index.js | 120 +++++++--------------------- tests/mobile-status-regressions.mjs | 2 + ui-actions-controller.js | 99 +++++++++++++++++++++++ 3 files changed, 128 insertions(+), 93 deletions(-) diff --git a/index.js b/index.js index f1da547..8aefde5 100644 --- a/index.js +++ b/index.js @@ -80,6 +80,7 @@ import { onFetchMemoryLLMModelsController, onExportGraphController, onManualCompressController, + onRebuildController, onTestEmbeddingController, onTestMemoryLLMController, onViewLastInjectionController, @@ -4492,99 +4493,32 @@ async function onViewGraph() { } async function onRebuild() { - if (!confirm("确定要从当前聊天重建图谱?这将清除现有图谱数据。")) return; - if (!ensureGraphMutationReady("重建图谱")) return; - - const context = getContext(); - const chat = context?.chat; - if (!Array.isArray(chat)) { - toastr.warning("当前聊天上下文不可用,无法重建"); - return; - } - - const previousGraphSnapshot = currentGraph - ? cloneGraphSnapshot(currentGraph) - : cloneGraphSnapshot( - normalizeGraphRuntimeState(createEmptyGraph(), getCurrentChatId()), - ); - const previousUiState = snapshotRuntimeUiState(); - const settings = getSettings(); - setRuntimeStatus( - "图谱重建中", - `当前聊天 ${Array.isArray(chat) ? chat.length : 0} 条消息`, - "running", - ); - - currentGraph = normalizeGraphRuntimeState( - createEmptyGraph(), - getCurrentChatId(), - ); - currentGraph.batchJournal = []; - clearInjectionState(); - - try { - await prepareVectorStateForReplay(true); - const replayedBatches = await replayExtractionFromHistory(chat, settings); - clearHistoryDirty( - currentGraph, - buildRecoveryResult("full-rebuild", { - fromFloor: 0, - batches: replayedBatches, - path: "full-rebuild", - detectionSource: "manual-rebuild", - affectedBatchCount: currentGraph.batchJournal?.length || 0, - replayedBatchCount: replayedBatches, - reason: "用户手动触发全量重建", - }), - ); - saveGraphToChat({ reason: "manual-rebuild-complete" }); - setLastExtractionStatus( - "图谱重建完成", - `已回放 ${replayedBatches} 批提取`, - "success", - { - syncRuntime: false, - }, - ); - - if (currentGraph.vectorIndexState?.lastWarning) { - setRuntimeStatus( - "图谱重建完成", - `已回放 ${replayedBatches} 批,但向量仍待修复`, - "warning", - ); - toastr.warning( - `图谱已重建,但向量索引仍待修复: ${currentGraph.vectorIndexState.lastWarning}`, - ); - } else { - setRuntimeStatus( - "图谱重建完成", - `已回放 ${replayedBatches} 批,图谱与向量索引已刷新`, - "success", - ); - toastr.success("图谱与向量索引已按当前聊天全量重建"); - } - } catch (error) { - currentGraph = normalizeGraphRuntimeState( - previousGraphSnapshot, - getCurrentChatId(), - ); - restoreRuntimeUiState(previousUiState); - saveGraphToChat({ reason: "manual-rebuild-restore-previous" }); - setLastExtractionStatus( - "图谱重建失败", - error?.message || String(error), - "error", - { - syncRuntime: true, - }, - ); - throw new Error( - `图谱重建失败,已恢复到重建前状态: ${error?.message || error}`, - ); - } finally { - refreshPanelLiveState(); - } + return await onRebuildController({ + buildRecoveryResult, + clearHistoryDirty, + clearInjectionState, + cloneGraphSnapshot, + confirm, + createEmptyGraph, + ensureGraphMutationReady, + getContext, + getCurrentChatId, + getCurrentGraph: () => currentGraph, + getSettings, + normalizeGraphRuntimeState, + prepareVectorStateForReplay, + refreshPanelLiveState, + replayExtractionFromHistory, + restoreRuntimeUiState, + saveGraphToChat, + setCurrentGraph: (graph) => { + currentGraph = graph; + }, + setLastExtractionStatus, + setRuntimeStatus, + snapshotRuntimeUiState, + toastr, + }); } async function onManualCompress() { diff --git a/tests/mobile-status-regressions.mjs b/tests/mobile-status-regressions.mjs index 035ca4a..3b7a11a 100644 --- a/tests/mobile-status-regressions.mjs +++ b/tests/mobile-status-regressions.mjs @@ -4,6 +4,7 @@ import path from "node:path"; import { fileURLToPath } from "node:url"; import vm from "node:vm"; import { onManualExtractController } from "../extraction-controller.js"; +import { onRebuildController } from "../ui-actions-controller.js"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const indexPath = path.resolve(__dirname, "../index.js"); @@ -240,6 +241,7 @@ async function testManualRebuildSetsTerminalRuntimeStatus() { }, saveGraphToChat() {}, restoreRuntimeUiState() {}, + onRebuildController, result: null, }; vm.createContext(context); diff --git a/ui-actions-controller.js b/ui-actions-controller.js index 8219c0d..04c88ff 100644 --- a/ui-actions-controller.js +++ b/ui-actions-controller.js @@ -146,3 +146,102 @@ export async function onViewLastInjectionController(runtime) { runtime.document.body.appendChild(popup); } + +export async function onRebuildController(runtime) { + if (!runtime.confirm("确定要从当前聊天重建图谱?这将清除现有图谱数据。")) { + return; + } + if (!runtime.ensureGraphMutationReady("重建图谱")) return; + + const context = runtime.getContext(); + const chat = context?.chat; + if (!Array.isArray(chat)) { + runtime.toastr.warning("当前聊天上下文不可用,无法重建"); + return; + } + + const previousGraphSnapshot = runtime.getCurrentGraph() + ? runtime.cloneGraphSnapshot(runtime.getCurrentGraph()) + : runtime.cloneGraphSnapshot( + runtime.normalizeGraphRuntimeState( + runtime.createEmptyGraph(), + runtime.getCurrentChatId(), + ), + ); + const previousUiState = runtime.snapshotRuntimeUiState(); + const settings = runtime.getSettings(); + runtime.setRuntimeStatus( + "图谱重建中", + `当前聊天 ${Array.isArray(chat) ? chat.length : 0} 条消息`, + "running", + ); + + const nextGraph = runtime.normalizeGraphRuntimeState( + runtime.createEmptyGraph(), + runtime.getCurrentChatId(), + ); + nextGraph.batchJournal = []; + runtime.setCurrentGraph(nextGraph); + runtime.clearInjectionState(); + + try { + await runtime.prepareVectorStateForReplay(true); + const replayedBatches = await runtime.replayExtractionFromHistory(chat, settings); + runtime.clearHistoryDirty( + runtime.getCurrentGraph(), + runtime.buildRecoveryResult("full-rebuild", { + fromFloor: 0, + batches: replayedBatches, + path: "full-rebuild", + detectionSource: "manual-rebuild", + affectedBatchCount: runtime.getCurrentGraph().batchJournal?.length || 0, + replayedBatchCount: replayedBatches, + reason: "用户手动触发全量重建", + }), + ); + runtime.saveGraphToChat({ reason: "manual-rebuild-complete" }); + runtime.setLastExtractionStatus( + "图谱重建完成", + `已回放 ${replayedBatches} 批提取`, + "success", + { + syncRuntime: false, + }, + ); + + if (runtime.getCurrentGraph().vectorIndexState?.lastWarning) { + runtime.setRuntimeStatus( + "图谱重建完成", + `已回放 ${replayedBatches} 批,但向量仍待修复`, + "warning", + ); + runtime.toastr.warning( + `图谱已重建,但向量索引仍待修复: ${runtime.getCurrentGraph().vectorIndexState.lastWarning}`, + ); + } else { + runtime.setRuntimeStatus( + "图谱重建完成", + `已回放 ${replayedBatches} 批,图谱与向量索引已刷新`, + "success", + ); + runtime.toastr.success("图谱与向量索引已按当前聊天全量重建"); + } + } catch (error) { + runtime.setCurrentGraph( + runtime.normalizeGraphRuntimeState( + previousGraphSnapshot, + runtime.getCurrentChatId(), + ), + ); + runtime.restoreRuntimeUiState(previousUiState); + runtime.saveGraphToChat({ reason: "manual-rebuild-restore-previous" }); + runtime.setLastExtractionStatus("图谱重建失败", error?.message || String(error), "error", { + syncRuntime: true, + }); + throw new Error( + `图谱重建失败,已恢复到重建前状态: ${error?.message || error}`, + ); + } finally { + runtime.refreshPanelLiveState(); + } +}