Fix graph metadata readiness detection

This commit is contained in:
Youzini-afk
2026-03-28 14:32:04 +08:00
parent 3cdfe01137
commit eb51f28d21
2 changed files with 106 additions and 56 deletions

126
index.js
View File

@@ -1174,11 +1174,13 @@ function hasLikelySelectedChatContext(context = getContext()) {
return false; return false;
} }
const hasChatMetadata = const hasMeaningfulChatMetadata =
context.chatMetadata && context.chatMetadata &&
typeof context.chatMetadata === "object" && typeof context.chatMetadata === "object" &&
!Array.isArray(context.chatMetadata); !Array.isArray(context.chatMetadata) &&
const hasChatMessages = Array.isArray(context.chat); Object.keys(context.chatMetadata).length > 0;
const hasChatMessages =
Array.isArray(context.chat) && context.chat.length > 0;
const hasCharacterId = const hasCharacterId =
context.characterId !== undefined && context.characterId !== undefined &&
context.characterId !== null && context.characterId !== null &&
@@ -1188,7 +1190,30 @@ function hasLikelySelectedChatContext(context = getContext()) {
context.groupId !== null && context.groupId !== null &&
String(context.groupId).trim() !== ""; String(context.groupId).trim() !== "";
return hasChatMetadata || hasChatMessages || hasCharacterId || hasGroupId; return (
hasMeaningfulChatMetadata ||
hasChatMessages ||
hasCharacterId ||
hasGroupId
);
}
function isHostChatMetadataReady(context = getContext()) {
if (
!context?.chatMetadata ||
typeof context.chatMetadata !== "object" ||
Array.isArray(context.chatMetadata)
) {
return false;
}
const metadata = context.chatMetadata;
// SillyTavern 在 CHAT_CHANGED 之前会为已加载聊天补上 integrity。
if (normalizeChatIdCandidate(metadata.integrity)) {
return true;
}
return Object.keys(metadata).length > 0;
} }
function resolveCurrentChatIdentity(context = getContext()) { function resolveCurrentChatIdentity(context = getContext()) {
@@ -2324,6 +2349,7 @@ function loadGraphFromChat(options = {}) {
context?.chatMetadata && context?.chatMetadata &&
typeof context.chatMetadata === "object" && typeof context.chatMetadata === "object" &&
!Array.isArray(context.chatMetadata); !Array.isArray(context.chatMetadata);
const metadataReady = isHostChatMetadataReady(context);
const savedData = hasChatMetadata const savedData = hasChatMetadata
? context.chatMetadata[GRAPH_METADATA_KEY] ? context.chatMetadata[GRAPH_METADATA_KEY]
: undefined; : undefined;
@@ -2432,7 +2458,7 @@ function loadGraphFromChat(options = {}) {
"warning", "warning",
); );
if (hasChatMetadata && !shouldRetry) { if (metadataReady) {
applyGraphLoadState(GRAPH_LOAD_STATES.LOADED, { applyGraphLoadState(GRAPH_LOAD_STATES.LOADED, {
chatId, chatId,
reason: "shadow-snapshot-promoted", reason: "shadow-snapshot-promoted",
@@ -2508,32 +2534,40 @@ function loadGraphFromChat(options = {}) {
lastRecalledItems = []; lastRecalledItems = [];
lastInjectionContent = ""; lastInjectionContent = "";
if (shouldRetry) { if (!metadataReady) {
runtimeStatus = createUiStatus( runtimeStatus = createUiStatus(
"待命", "待命",
"正在加载当前聊天图谱,暂不写回图谱元数据", shouldRetry
"idle", ? "正在加载当前聊天图谱,暂不写回图谱元数据"
: "聊天元数据未就绪,已暂停图谱写回",
shouldRetry ? "idle" : "warning",
); );
lastExtractionStatus = createUiStatus( lastExtractionStatus = createUiStatus(
"待命", "待命",
"正在等待聊天图谱元数据加载", shouldRetry ? "正在等待聊天图谱元数据加载" : "聊天元数据未就绪,暂不允许修改图谱",
"idle", shouldRetry ? "idle" : "warning",
); );
lastVectorStatus = createUiStatus( lastVectorStatus = createUiStatus(
"待命", "待命",
"正在等待聊天图谱元数据加载", shouldRetry ? "正在等待聊天图谱元数据加载" : "聊天元数据未就绪,暂不允许修改图谱",
"idle", shouldRetry ? "idle" : "warning",
); );
lastRecallStatus = createUiStatus( lastRecallStatus = createUiStatus(
"待命", "待命",
"正在等待聊天图谱元数据加载", shouldRetry ? "正在等待聊天图谱元数据加载" : "聊天元数据未就绪,图谱处于保护状态",
"idle", shouldRetry ? "idle" : "warning",
); );
applyGraphLoadState(GRAPH_LOAD_STATES.LOADING, { applyGraphLoadState(
shouldRetry ? GRAPH_LOAD_STATES.LOADING : GRAPH_LOAD_STATES.BLOCKED,
{
chatId, chatId,
reason: hasChatMetadata reason: hasChatMetadata
? "graph-metadata-missing" ? shouldRetry
: "chat-metadata-missing", ? "graph-metadata-missing"
: "graph-metadata-timeout"
: shouldRetry
? "chat-metadata-missing"
: "chat-metadata-timeout",
attemptIndex, attemptIndex,
revision: 0, revision: 0,
lastPersistedRevision: 0, lastPersistedRevision: 0,
@@ -2544,17 +2578,24 @@ function loadGraphFromChat(options = {}) {
shadowSnapshotUpdatedAt: "", shadowSnapshotUpdatedAt: "",
shadowSnapshotReason: "", shadowSnapshotReason: "",
writesBlocked: true, writesBlocked: true,
}); },
scheduleGraphLoadRetry(
chatId,
hasChatMetadata ? "graph-metadata-missing" : "chat-metadata-missing",
attemptIndex,
); );
if (shouldRetry) {
scheduleGraphLoadRetry(
chatId,
hasChatMetadata ? "graph-metadata-missing" : "chat-metadata-missing",
attemptIndex,
);
} else {
clearPendingGraphLoadRetry();
}
refreshPanelLiveState(); refreshPanelLiveState();
return { return {
success: false, success: false,
loaded: false, loaded: false,
loadState: GRAPH_LOAD_STATES.LOADING, loadState: shouldRetry
? GRAPH_LOAD_STATES.LOADING
: GRAPH_LOAD_STATES.BLOCKED,
reason: graphPersistenceState.reason, reason: graphPersistenceState.reason,
chatId, chatId,
attemptIndex, attemptIndex,
@@ -2563,43 +2604,30 @@ function loadGraphFromChat(options = {}) {
} }
clearPendingGraphLoadRetry(); clearPendingGraphLoadRetry();
const confirmedState = hasChatMetadata const confirmedState = GRAPH_LOAD_STATES.EMPTY_CONFIRMED;
? GRAPH_LOAD_STATES.EMPTY_CONFIRMED
: GRAPH_LOAD_STATES.BLOCKED;
runtimeStatus = createUiStatus( runtimeStatus = createUiStatus(
"待命", "待命",
confirmedState === GRAPH_LOAD_STATES.EMPTY_CONFIRMED "当前聊天还没有图谱",
? "当前聊天还没有图谱" "idle",
: "聊天元数据未就绪,已暂停图谱写回",
confirmedState === GRAPH_LOAD_STATES.EMPTY_CONFIRMED ? "idle" : "warning",
); );
lastExtractionStatus = createUiStatus( lastExtractionStatus = createUiStatus(
"待命", "待命",
confirmedState === GRAPH_LOAD_STATES.EMPTY_CONFIRMED "当前聊天尚未执行提取",
? "当前聊天尚未执行提取" "idle",
: "聊天元数据未就绪,暂不允许修改图谱",
confirmedState === GRAPH_LOAD_STATES.EMPTY_CONFIRMED ? "idle" : "warning",
); );
lastVectorStatus = createUiStatus( lastVectorStatus = createUiStatus(
"待命", "待命",
confirmedState === GRAPH_LOAD_STATES.EMPTY_CONFIRMED "当前聊天尚未执行向量任务",
? "当前聊天尚未执行向量任务" "idle",
: "聊天元数据未就绪,暂不允许修改图谱",
confirmedState === GRAPH_LOAD_STATES.EMPTY_CONFIRMED ? "idle" : "warning",
); );
lastRecallStatus = createUiStatus( lastRecallStatus = createUiStatus(
"待命", "待命",
confirmedState === GRAPH_LOAD_STATES.EMPTY_CONFIRMED "当前聊天尚未建立记忆图谱",
? "当前聊天尚未建立记忆图谱" "idle",
: "聊天元数据未就绪,图谱处于保护状态",
confirmedState === GRAPH_LOAD_STATES.EMPTY_CONFIRMED ? "idle" : "warning",
); );
applyGraphLoadState(confirmedState, { applyGraphLoadState(confirmedState, {
chatId, chatId,
reason: reason: "metadata-confirmed-empty",
confirmedState === GRAPH_LOAD_STATES.EMPTY_CONFIRMED
? "metadata-confirmed-empty"
: "chat-metadata-timeout",
attemptIndex, attemptIndex,
revision: 0, revision: 0,
lastPersistedRevision: 0, lastPersistedRevision: 0,
@@ -2609,11 +2637,9 @@ function loadGraphFromChat(options = {}) {
shadowSnapshotRevision: 0, shadowSnapshotRevision: 0,
shadowSnapshotUpdatedAt: "", shadowSnapshotUpdatedAt: "",
shadowSnapshotReason: "", shadowSnapshotReason: "",
writesBlocked: confirmedState !== GRAPH_LOAD_STATES.EMPTY_CONFIRMED, writesBlocked: false,
}); });
if (confirmedState === GRAPH_LOAD_STATES.EMPTY_CONFIRMED) { removeGraphShadowSnapshot(chatId);
removeGraphShadowSnapshot(chatId);
}
refreshPanelLiveState(); refreshPanelLiveState();
return { return {
success: false, success: false,

View File

@@ -267,6 +267,26 @@ result = {
}; };
} }
{
const harness = await createGraphPersistenceHarness({
chatId: "",
globalChatId: "",
chatMetadata: {},
characterId: "",
groupId: null,
chat: [],
});
const result = harness.api.loadGraphFromChat({
attemptIndex: 0,
source: "no-chat-empty-host-state",
});
const live = harness.api.getGraphPersistenceLiveState();
assert.equal(result.loadState, "no-chat");
assert.equal(live.loadState, "no-chat");
assert.equal(live.writesBlocked, true);
}
{ {
const harness = await createGraphPersistenceHarness({ const harness = await createGraphPersistenceHarness({
chatId: "", chatId: "",
@@ -466,11 +486,13 @@ result = {
{ {
const harness = await createGraphPersistenceHarness({ const harness = await createGraphPersistenceHarness({
chatId: "chat-empty-confirmed", chatId: "chat-empty-confirmed",
chatMetadata: {}, chatMetadata: {
integrity: "meta-ready-empty",
},
}); });
const result = harness.api.loadGraphFromChat({ const result = harness.api.loadGraphFromChat({
attemptIndex: harness.api.GRAPH_LOAD_RETRY_DELAYS_MS.length, attemptIndex: 0,
source: "timeout-empty", source: "ready-empty",
}); });
const live = harness.api.getGraphPersistenceLiveState(); const live = harness.api.getGraphPersistenceLiveState();
@@ -499,12 +521,14 @@ result = {
const reader = await createGraphPersistenceHarness({ const reader = await createGraphPersistenceHarness({
chatId: "chat-promote", chatId: "chat-promote",
chatMetadata: {}, chatMetadata: {
integrity: "meta-ready-promote",
},
sessionStore: sharedSession, sessionStore: sharedSession,
}); });
const result = reader.api.loadGraphFromChat({ const result = reader.api.loadGraphFromChat({
attemptIndex: reader.api.GRAPH_LOAD_RETRY_DELAYS_MS.length, attemptIndex: 0,
source: "promote-after-timeout", source: "promote-when-metadata-ready",
}); });
const live = reader.api.getGraphPersistenceLiveState(); const live = reader.api.getGraphPersistenceLiveState();