mirror of
https://github.com/Youzini-afk/ST-Bionic-Memory-Ecology.git
synced 2026-05-15 22:30:38 +08:00
fix: harden metadata readiness and add persistence self-heal reconcile
This commit is contained in:
@@ -263,6 +263,17 @@ export async function onBeforeCombinePromptsController(runtime) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function onMessageReceivedController(runtime) {
|
export function onMessageReceivedController(runtime) {
|
||||||
|
const loadState = runtime.getGraphPersistenceState?.()?.loadState || "";
|
||||||
|
if (
|
||||||
|
loadState === "loading" ||
|
||||||
|
loadState === "shadow-restored" ||
|
||||||
|
loadState === "blocked"
|
||||||
|
) {
|
||||||
|
runtime.syncGraphLoadFromLiveContext?.({
|
||||||
|
source: "message-received-reconcile",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (runtime.getCurrentGraph()) {
|
if (runtime.getCurrentGraph()) {
|
||||||
if (
|
if (
|
||||||
runtime.getGraphPersistenceState()?.pendingPersist &&
|
runtime.getGraphPersistenceState()?.pendingPersist &&
|
||||||
|
|||||||
35
index.js
35
index.js
@@ -1780,6 +1780,32 @@ function hasLikelySelectedChatContext(context = getContext()) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hasHostMetadataReadySignal(metadata = {}) {
|
||||||
|
if (!metadata || typeof metadata !== "object" || Array.isArray(metadata)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (normalizeChatIdCandidate(metadata.integrity)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const chatIdentityCandidates = [
|
||||||
|
metadata.chat_id,
|
||||||
|
metadata.chatId,
|
||||||
|
metadata.session_id,
|
||||||
|
metadata.sessionId,
|
||||||
|
];
|
||||||
|
if (
|
||||||
|
chatIdentityCandidates.some((candidate) =>
|
||||||
|
Boolean(normalizeChatIdCandidate(candidate)),
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
function isHostChatMetadataReady(context = getContext()) {
|
function isHostChatMetadataReady(context = getContext()) {
|
||||||
if (
|
if (
|
||||||
!context?.chatMetadata ||
|
!context?.chatMetadata ||
|
||||||
@@ -1790,12 +1816,10 @@ function isHostChatMetadataReady(context = getContext()) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const metadata = context.chatMetadata;
|
const metadata = context.chatMetadata;
|
||||||
// SillyTavern 在 CHAT_CHANGED 之前会为已加载聊天补上 integrity。
|
// 仅接受宿主“强信号”,避免把中间态/占位 metadata 误判为 ready。
|
||||||
if (normalizeChatIdCandidate(metadata.integrity)) {
|
if (hasHostMetadataReadySignal(metadata)) return true;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Object.keys(metadata).length > 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveCurrentChatIdentity(context = getContext()) {
|
function resolveCurrentChatIdentity(context = getContext()) {
|
||||||
@@ -5139,6 +5163,7 @@ function onMessageReceived() {
|
|||||||
isAssistantChatMessage,
|
isAssistantChatMessage,
|
||||||
isFreshRecallInputRecord,
|
isFreshRecallInputRecord,
|
||||||
isGraphMetadataWriteAllowed,
|
isGraphMetadataWriteAllowed,
|
||||||
|
syncGraphLoadFromLiveContext,
|
||||||
maybeCaptureGraphShadowSnapshot,
|
maybeCaptureGraphShadowSnapshot,
|
||||||
maybeFlushQueuedGraphPersist,
|
maybeFlushQueuedGraphPersist,
|
||||||
notifyExtractionIssue,
|
notifyExtractionIssue,
|
||||||
|
|||||||
@@ -526,6 +526,52 @@ result = {
|
|||||||
assert.equal(result.loadState, "empty-confirmed");
|
assert.equal(result.loadState, "empty-confirmed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const harness = await createGraphPersistenceHarness({
|
||||||
|
chatId: "chat-metadata-placeholder",
|
||||||
|
chatMetadata: {
|
||||||
|
placeholder: "host-loading",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const result = harness.api.loadGraphFromChat({
|
||||||
|
attemptIndex: 0,
|
||||||
|
source: "metadata-placeholder-not-ready",
|
||||||
|
});
|
||||||
|
const live = harness.api.getGraphPersistenceLiveState();
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
result.loadState,
|
||||||
|
"loading",
|
||||||
|
"无 integrity 的占位 metadata 不能视作 ready",
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
result.reason,
|
||||||
|
"graph-metadata-missing",
|
||||||
|
"应继续等待正式 graph metadata",
|
||||||
|
);
|
||||||
|
assert.equal(live.writesBlocked, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const harness = await createGraphPersistenceHarness({
|
||||||
|
chatId: "chat-metadata-chatid-ready",
|
||||||
|
chatMetadata: {
|
||||||
|
chatId: "chat-metadata-chatid-ready",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const result = harness.api.loadGraphFromChat({
|
||||||
|
attemptIndex: 0,
|
||||||
|
source: "metadata-chatid-ready",
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(result.loadState, "empty-confirmed");
|
||||||
|
assert.equal(
|
||||||
|
harness.api.getGraphPersistenceLiveState().writesBlocked,
|
||||||
|
false,
|
||||||
|
"当 metadata 提供 chatId/sessionId 等强信号时,可进入 ready-empty",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const harness = await createGraphPersistenceHarness({
|
const harness = await createGraphPersistenceHarness({
|
||||||
chatId: "",
|
chatId: "",
|
||||||
@@ -640,6 +686,45 @@ result = {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const harness = await createGraphPersistenceHarness({
|
||||||
|
chatId: "chat-late-reconcile",
|
||||||
|
chatMetadata: undefined,
|
||||||
|
});
|
||||||
|
harness.api.setCurrentGraph(
|
||||||
|
normalizeGraphRuntimeState(createEmptyGraph(), "chat-late-reconcile"),
|
||||||
|
);
|
||||||
|
harness.api.setGraphPersistenceState({
|
||||||
|
loadState: "blocked",
|
||||||
|
chatId: "chat-late-reconcile",
|
||||||
|
reason: "chat-metadata-timeout",
|
||||||
|
revision: 2,
|
||||||
|
writesBlocked: true,
|
||||||
|
});
|
||||||
|
harness.api.setChatContext({
|
||||||
|
...harness.api.getChatContext(),
|
||||||
|
chatId: "chat-late-reconcile",
|
||||||
|
chatMetadata: {
|
||||||
|
integrity: "chat-late-reconcile-ready",
|
||||||
|
st_bme_graph: createMeaningfulGraph("chat-late-reconcile", "late-official"),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
harness.api.onMessageReceived();
|
||||||
|
|
||||||
|
const live = harness.api.getGraphPersistenceLiveState();
|
||||||
|
assert.equal(
|
||||||
|
live.loadState,
|
||||||
|
"loaded",
|
||||||
|
"BLOCKED 后 onMessageReceived 应触发元数据重探测并自动恢复",
|
||||||
|
);
|
||||||
|
assert.equal(live.writesBlocked, false);
|
||||||
|
assert.equal(
|
||||||
|
harness.api.getCurrentGraph().nodes[0]?.fields?.title,
|
||||||
|
"事件-late-official",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const sharedSession = new Map();
|
const sharedSession = new Map();
|
||||||
const writer = await createGraphPersistenceHarness({
|
const writer = await createGraphPersistenceHarness({
|
||||||
|
|||||||
Reference in New Issue
Block a user