mirror of
https://github.com/Youzini-afk/ST-Bionic-Memory-Ecology.git
synced 2026-05-15 22:30:38 +08:00
Fix stale pending persistence gate
This commit is contained in:
@@ -176,12 +176,31 @@ function buildCommittedBatchPersistSnapshot(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isPersistenceRevisionAccepted(runtime, persistence = null) {
|
||||||
|
if (!persistence || persistence.accepted === true) return true;
|
||||||
|
const graphPersistenceState = runtime?.getGraphPersistenceState?.() || {};
|
||||||
|
if (graphPersistenceState.pendingPersist === true) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const persistenceRevision = Number(persistence?.revision || 0);
|
||||||
|
if (!Number.isFinite(persistenceRevision) || persistenceRevision <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const lastAcceptedRevision = Math.max(
|
||||||
|
Number(graphPersistenceState?.lastAcceptedRevision || 0),
|
||||||
|
Number(graphPersistenceState?.commitMarker?.accepted === true
|
||||||
|
? graphPersistenceState?.commitMarker?.revision
|
||||||
|
: 0),
|
||||||
|
);
|
||||||
|
return Number.isFinite(lastAcceptedRevision) && lastAcceptedRevision >= persistenceRevision;
|
||||||
|
}
|
||||||
|
|
||||||
function getPendingPersistenceGateInfo(runtime) {
|
function getPendingPersistenceGateInfo(runtime) {
|
||||||
const graph = runtime?.getCurrentGraph?.();
|
const graph = runtime?.getCurrentGraph?.();
|
||||||
const batchStatus = graph?.historyState?.lastBatchStatus || null;
|
const batchStatus = graph?.historyState?.lastBatchStatus || null;
|
||||||
const persistence = batchStatus?.persistence || null;
|
const persistence = batchStatus?.persistence || null;
|
||||||
const pendingPersist = runtime?.getGraphPersistenceState?.()?.pendingPersist === true;
|
const pendingPersist = runtime?.getGraphPersistenceState?.()?.pendingPersist === true;
|
||||||
const accepted = persistence?.accepted === true;
|
const accepted = isPersistenceRevisionAccepted(runtime, persistence);
|
||||||
if (!pendingPersist && (!persistence || accepted)) {
|
if (!pendingPersist && (!persistence || accepted)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -206,6 +206,129 @@ async function testManualExtractNoBatchesDoesNotStayRunning() {
|
|||||||
assert.notEqual(context.runtimeStatus.level, "running");
|
assert.notEqual(context.runtimeStatus.level, "running");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function testManualExtractIgnoresSupersededPendingPersistence() {
|
||||||
|
let executeExtractionBatchCalls = 0;
|
||||||
|
let assistantTurnCallCount = 0;
|
||||||
|
const chat = [{ is_user: true, mes: "u" }, { is_user: false, mes: "a" }];
|
||||||
|
const context = {
|
||||||
|
...createBaseStatusContext(),
|
||||||
|
isExtracting: false,
|
||||||
|
graphPersistenceState: {
|
||||||
|
pendingPersist: false,
|
||||||
|
lastAcceptedRevision: 7,
|
||||||
|
},
|
||||||
|
currentGraph: {
|
||||||
|
historyState: {
|
||||||
|
lastBatchStatus: {
|
||||||
|
processedRange: [1, 1],
|
||||||
|
persistence: {
|
||||||
|
outcome: "queued",
|
||||||
|
accepted: false,
|
||||||
|
revision: 7,
|
||||||
|
reason: "extraction-batch-complete:pending",
|
||||||
|
storageTier: "none",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
getCurrentChatId() {
|
||||||
|
return "chat-mobile";
|
||||||
|
},
|
||||||
|
getCurrentGraph() {
|
||||||
|
return context.currentGraph;
|
||||||
|
},
|
||||||
|
getIsExtracting() {
|
||||||
|
return context.isExtracting;
|
||||||
|
},
|
||||||
|
getGraphPersistenceState() {
|
||||||
|
return {
|
||||||
|
pendingPersist: false,
|
||||||
|
lastAcceptedRevision: 7,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
ensureGraphMutationReady() {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
async recoverHistoryIfNeeded() {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
normalizeGraphRuntimeState(graph) {
|
||||||
|
return graph;
|
||||||
|
},
|
||||||
|
setCurrentGraph(graph) {
|
||||||
|
context.currentGraph = graph;
|
||||||
|
},
|
||||||
|
createEmptyGraph() {
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
getContext() {
|
||||||
|
return { chat };
|
||||||
|
},
|
||||||
|
getAssistantTurns() {
|
||||||
|
assistantTurnCallCount += 1;
|
||||||
|
return assistantTurnCallCount <= 2 ? [1] : [];
|
||||||
|
},
|
||||||
|
getLastProcessedAssistantFloor() {
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
clampInt(value, fallback) {
|
||||||
|
return Number.isFinite(Number(value)) ? Number(value) : fallback;
|
||||||
|
},
|
||||||
|
getSettings() {
|
||||||
|
return { extractEvery: 1 };
|
||||||
|
},
|
||||||
|
beginStageAbortController() {
|
||||||
|
return { signal: {} };
|
||||||
|
},
|
||||||
|
async executeExtractionBatch() {
|
||||||
|
executeExtractionBatchCalls += 1;
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
result: {
|
||||||
|
newNodes: 0,
|
||||||
|
updatedNodes: 0,
|
||||||
|
newEdges: 0,
|
||||||
|
},
|
||||||
|
effects: {},
|
||||||
|
batchStatus: {
|
||||||
|
persistence: {
|
||||||
|
accepted: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
historyAdvanceAllowed: true,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
async retryPendingGraphPersist() {
|
||||||
|
return {
|
||||||
|
accepted: false,
|
||||||
|
reason: "no-pending-persist",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
isAbortError() {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
onManualExtractController,
|
||||||
|
finishStageAbortController() {},
|
||||||
|
setIsExtracting(value) {
|
||||||
|
context.isExtracting = value;
|
||||||
|
},
|
||||||
|
setLastExtractionStatus(text, meta, level) {
|
||||||
|
context.lastExtractionStatus = { text, meta, level };
|
||||||
|
context.runtimeStatus = { text, meta, level };
|
||||||
|
},
|
||||||
|
toastr: {
|
||||||
|
info() {},
|
||||||
|
success() {},
|
||||||
|
warning() {},
|
||||||
|
error() {},
|
||||||
|
},
|
||||||
|
result: null,
|
||||||
|
};
|
||||||
|
await onManualExtractController(context, { drainAll: false });
|
||||||
|
assert.equal(executeExtractionBatchCalls, 1);
|
||||||
|
assert.notEqual(context.lastExtractionStatus.text, "等待持久化确认");
|
||||||
|
}
|
||||||
|
|
||||||
async function testManualRebuildSetsTerminalRuntimeStatus() {
|
async function testManualRebuildSetsTerminalRuntimeStatus() {
|
||||||
const chat = [{ is_user: true, mes: "u" }, { is_user: false, mes: "a" }];
|
const chat = [{ is_user: true, mes: "u" }, { is_user: false, mes: "a" }];
|
||||||
const context = {
|
const context = {
|
||||||
@@ -281,6 +404,7 @@ async function testManualRebuildSetsTerminalRuntimeStatus() {
|
|||||||
testIndexDefinesLastProcessedAssistantFloorHelper();
|
testIndexDefinesLastProcessedAssistantFloorHelper();
|
||||||
await testVectorSyncTerminalStateUpdatesRuntime();
|
await testVectorSyncTerminalStateUpdatesRuntime();
|
||||||
await testManualExtractNoBatchesDoesNotStayRunning();
|
await testManualExtractNoBatchesDoesNotStayRunning();
|
||||||
|
await testManualExtractIgnoresSupersededPendingPersistence();
|
||||||
await testManualRebuildSetsTerminalRuntimeStatus();
|
await testManualRebuildSetsTerminalRuntimeStatus();
|
||||||
|
|
||||||
console.log("mobile-status-regressions tests passed");
|
console.log("mobile-status-regressions tests passed");
|
||||||
|
|||||||
24
ui/panel.js
24
ui/panel.js
@@ -9442,11 +9442,30 @@ function _formatPersistenceOutcomeLabel(outcome = "") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _isPersistenceRevisionAccepted(persistence = null, loadInfo = {}) {
|
||||||
|
if (!persistence || persistence.accepted === true) return true;
|
||||||
|
if (loadInfo?.pendingPersist === true) return false;
|
||||||
|
const persistenceRevision = Number(persistence?.revision || 0);
|
||||||
|
if (!Number.isFinite(persistenceRevision) || persistenceRevision <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const commitMarkerRevision =
|
||||||
|
loadInfo?.commitMarker?.accepted === true
|
||||||
|
? Number(loadInfo.commitMarker.revision || 0)
|
||||||
|
: 0;
|
||||||
|
const lastAcceptedRevision = Math.max(
|
||||||
|
Number(loadInfo?.lastAcceptedRevision || 0),
|
||||||
|
commitMarkerRevision,
|
||||||
|
);
|
||||||
|
return Number.isFinite(lastAcceptedRevision) && lastAcceptedRevision >= persistenceRevision;
|
||||||
|
}
|
||||||
|
|
||||||
function _formatDashboardPersistMeta(loadInfo = {}, batchStatus = null) {
|
function _formatDashboardPersistMeta(loadInfo = {}, batchStatus = null) {
|
||||||
const persistence = batchStatus?.persistence || null;
|
const persistence = batchStatus?.persistence || null;
|
||||||
if (persistence) {
|
if (persistence) {
|
||||||
|
const accepted = _isPersistenceRevisionAccepted(persistence, loadInfo);
|
||||||
const parts = [
|
const parts = [
|
||||||
_formatPersistenceOutcomeLabel(persistence.outcome),
|
accepted ? "已确认" : _formatPersistenceOutcomeLabel(persistence.outcome),
|
||||||
persistence.storageTier ? `tier ${persistence.storageTier}` : "",
|
persistence.storageTier ? `tier ${persistence.storageTier}` : "",
|
||||||
Number.isFinite(Number(persistence.revision)) && Number(persistence.revision) > 0
|
Number.isFinite(Number(persistence.revision)) && Number(persistence.revision) > 0
|
||||||
? `rev ${Number(persistence.revision)}`
|
? `rev ${Number(persistence.revision)}`
|
||||||
@@ -9477,6 +9496,7 @@ function _formatDashboardHistoryMeta(graph = null, loadInfo = {}, batchStatus =
|
|||||||
const lastConfirmedFloor =
|
const lastConfirmedFloor =
|
||||||
graph?.historyState?.lastProcessedAssistantFloor ?? -1;
|
graph?.historyState?.lastProcessedAssistantFloor ?? -1;
|
||||||
const persistence = batchStatus?.persistence || null;
|
const persistence = batchStatus?.persistence || null;
|
||||||
|
const accepted = _isPersistenceRevisionAccepted(persistence, loadInfo);
|
||||||
const processedRange = Array.isArray(batchStatus?.processedRange)
|
const processedRange = Array.isArray(batchStatus?.processedRange)
|
||||||
? batchStatus.processedRange
|
? batchStatus.processedRange
|
||||||
: [];
|
: [];
|
||||||
@@ -9485,7 +9505,7 @@ function _formatDashboardHistoryMeta(graph = null, loadInfo = {}, batchStatus =
|
|||||||
? Number(processedRange[1])
|
? Number(processedRange[1])
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
if (persistence && persistence.accepted !== true && pendingFloor != null) {
|
if (persistence && !accepted && pendingFloor != null) {
|
||||||
return `持久化待确认:本地已抽取到楼层 ${pendingFloor},已确认楼层 ${lastConfirmedFloor}`;
|
return `持久化待确认:本地已抽取到楼层 ${pendingFloor},已确认楼层 ${lastConfirmedFloor}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user