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;
}
const hasChatMetadata =
const hasMeaningfulChatMetadata =
context.chatMetadata &&
typeof context.chatMetadata === "object" &&
!Array.isArray(context.chatMetadata);
const hasChatMessages = Array.isArray(context.chat);
!Array.isArray(context.chatMetadata) &&
Object.keys(context.chatMetadata).length > 0;
const hasChatMessages =
Array.isArray(context.chat) && context.chat.length > 0;
const hasCharacterId =
context.characterId !== undefined &&
context.characterId !== null &&
@@ -1188,7 +1190,30 @@ function hasLikelySelectedChatContext(context = getContext()) {
context.groupId !== null &&
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()) {
@@ -2324,6 +2349,7 @@ function loadGraphFromChat(options = {}) {
context?.chatMetadata &&
typeof context.chatMetadata === "object" &&
!Array.isArray(context.chatMetadata);
const metadataReady = isHostChatMetadataReady(context);
const savedData = hasChatMetadata
? context.chatMetadata[GRAPH_METADATA_KEY]
: undefined;
@@ -2432,7 +2458,7 @@ function loadGraphFromChat(options = {}) {
"warning",
);
if (hasChatMetadata && !shouldRetry) {
if (metadataReady) {
applyGraphLoadState(GRAPH_LOAD_STATES.LOADED, {
chatId,
reason: "shadow-snapshot-promoted",
@@ -2508,32 +2534,40 @@ function loadGraphFromChat(options = {}) {
lastRecalledItems = [];
lastInjectionContent = "";
if (shouldRetry) {
if (!metadataReady) {
runtimeStatus = createUiStatus(
"待命",
"正在加载当前聊天图谱,暂不写回图谱元数据",
"idle",
shouldRetry
? "正在加载当前聊天图谱,暂不写回图谱元数据"
: "聊天元数据未就绪,已暂停图谱写回",
shouldRetry ? "idle" : "warning",
);
lastExtractionStatus = createUiStatus(
"待命",
"正在等待聊天图谱元数据加载",
"idle",
shouldRetry ? "正在等待聊天图谱元数据加载" : "聊天元数据未就绪,暂不允许修改图谱",
shouldRetry ? "idle" : "warning",
);
lastVectorStatus = createUiStatus(
"待命",
"正在等待聊天图谱元数据加载",
"idle",
shouldRetry ? "正在等待聊天图谱元数据加载" : "聊天元数据未就绪,暂不允许修改图谱",
shouldRetry ? "idle" : "warning",
);
lastRecallStatus = createUiStatus(
"待命",
"正在等待聊天图谱元数据加载",
"idle",
shouldRetry ? "正在等待聊天图谱元数据加载" : "聊天元数据未就绪,图谱处于保护状态",
shouldRetry ? "idle" : "warning",
);
applyGraphLoadState(GRAPH_LOAD_STATES.LOADING, {
applyGraphLoadState(
shouldRetry ? GRAPH_LOAD_STATES.LOADING : GRAPH_LOAD_STATES.BLOCKED,
{
chatId,
reason: hasChatMetadata
? "graph-metadata-missing"
: "chat-metadata-missing",
? shouldRetry
? "graph-metadata-missing"
: "graph-metadata-timeout"
: shouldRetry
? "chat-metadata-missing"
: "chat-metadata-timeout",
attemptIndex,
revision: 0,
lastPersistedRevision: 0,
@@ -2544,17 +2578,24 @@ function loadGraphFromChat(options = {}) {
shadowSnapshotUpdatedAt: "",
shadowSnapshotReason: "",
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();
return {
success: false,
loaded: false,
loadState: GRAPH_LOAD_STATES.LOADING,
loadState: shouldRetry
? GRAPH_LOAD_STATES.LOADING
: GRAPH_LOAD_STATES.BLOCKED,
reason: graphPersistenceState.reason,
chatId,
attemptIndex,
@@ -2563,43 +2604,30 @@ function loadGraphFromChat(options = {}) {
}
clearPendingGraphLoadRetry();
const confirmedState = hasChatMetadata
? GRAPH_LOAD_STATES.EMPTY_CONFIRMED
: GRAPH_LOAD_STATES.BLOCKED;
const confirmedState = GRAPH_LOAD_STATES.EMPTY_CONFIRMED;
runtimeStatus = createUiStatus(
"待命",
confirmedState === GRAPH_LOAD_STATES.EMPTY_CONFIRMED
? "当前聊天还没有图谱"
: "聊天元数据未就绪,已暂停图谱写回",
confirmedState === GRAPH_LOAD_STATES.EMPTY_CONFIRMED ? "idle" : "warning",
"当前聊天还没有图谱",
"idle",
);
lastExtractionStatus = createUiStatus(
"待命",
confirmedState === GRAPH_LOAD_STATES.EMPTY_CONFIRMED
? "当前聊天尚未执行提取"
: "聊天元数据未就绪,暂不允许修改图谱",
confirmedState === GRAPH_LOAD_STATES.EMPTY_CONFIRMED ? "idle" : "warning",
"当前聊天尚未执行提取",
"idle",
);
lastVectorStatus = createUiStatus(
"待命",
confirmedState === GRAPH_LOAD_STATES.EMPTY_CONFIRMED
? "当前聊天尚未执行向量任务"
: "聊天元数据未就绪,暂不允许修改图谱",
confirmedState === GRAPH_LOAD_STATES.EMPTY_CONFIRMED ? "idle" : "warning",
"当前聊天尚未执行向量任务",
"idle",
);
lastRecallStatus = createUiStatus(
"待命",
confirmedState === GRAPH_LOAD_STATES.EMPTY_CONFIRMED
? "当前聊天尚未建立记忆图谱"
: "聊天元数据未就绪,图谱处于保护状态",
confirmedState === GRAPH_LOAD_STATES.EMPTY_CONFIRMED ? "idle" : "warning",
"当前聊天尚未建立记忆图谱",
"idle",
);
applyGraphLoadState(confirmedState, {
chatId,
reason:
confirmedState === GRAPH_LOAD_STATES.EMPTY_CONFIRMED
? "metadata-confirmed-empty"
: "chat-metadata-timeout",
reason: "metadata-confirmed-empty",
attemptIndex,
revision: 0,
lastPersistedRevision: 0,
@@ -2609,11 +2637,9 @@ function loadGraphFromChat(options = {}) {
shadowSnapshotRevision: 0,
shadowSnapshotUpdatedAt: "",
shadowSnapshotReason: "",
writesBlocked: confirmedState !== GRAPH_LOAD_STATES.EMPTY_CONFIRMED,
writesBlocked: false,
});
if (confirmedState === GRAPH_LOAD_STATES.EMPTY_CONFIRMED) {
removeGraphShadowSnapshot(chatId);
}
removeGraphShadowSnapshot(chatId);
refreshPanelLiveState();
return {
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({
chatId: "",
@@ -466,11 +486,13 @@ result = {
{
const harness = await createGraphPersistenceHarness({
chatId: "chat-empty-confirmed",
chatMetadata: {},
chatMetadata: {
integrity: "meta-ready-empty",
},
});
const result = harness.api.loadGraphFromChat({
attemptIndex: harness.api.GRAPH_LOAD_RETRY_DELAYS_MS.length,
source: "timeout-empty",
attemptIndex: 0,
source: "ready-empty",
});
const live = harness.api.getGraphPersistenceLiveState();
@@ -499,12 +521,14 @@ result = {
const reader = await createGraphPersistenceHarness({
chatId: "chat-promote",
chatMetadata: {},
chatMetadata: {
integrity: "meta-ready-promote",
},
sessionStore: sharedSession,
});
const result = reader.api.loadGraphFromChat({
attemptIndex: reader.api.GRAPH_LOAD_RETRY_DELAYS_MS.length,
source: "promote-after-timeout",
attemptIndex: 0,
source: "promote-when-metadata-ready",
});
const live = reader.api.getGraphPersistenceLiveState();