mirror of
https://github.com/Youzini-afk/ST-Bionic-Memory-Ecology.git
synced 2026-05-15 22:30:38 +08:00
fix recall card binding lag
This commit is contained in:
@@ -219,11 +219,32 @@ export function onChatLoadedController(runtime) {
|
|||||||
export function onMessageSentController(runtime, messageId) {
|
export function onMessageSentController(runtime, messageId) {
|
||||||
const context = runtime.getContext();
|
const context = runtime.getContext();
|
||||||
const chat = context?.chat;
|
const chat = context?.chat;
|
||||||
const message =
|
const normalizedMessageId =
|
||||||
Array.isArray(chat) && Number.isFinite(messageId) ? chat[messageId] : null;
|
messageId === null || messageId === undefined || messageId === ""
|
||||||
|
? null
|
||||||
|
: Number(messageId);
|
||||||
|
let resolvedMessageId = Number.isFinite(normalizedMessageId)
|
||||||
|
? normalizedMessageId
|
||||||
|
: null;
|
||||||
|
let message =
|
||||||
|
Array.isArray(chat) && Number.isFinite(resolvedMessageId)
|
||||||
|
? chat[resolvedMessageId]
|
||||||
|
: null;
|
||||||
|
|
||||||
|
if (!message?.is_user && Array.isArray(chat)) {
|
||||||
|
for (let index = chat.length - 1; index >= 0; index--) {
|
||||||
|
if (!chat[index]?.is_user) continue;
|
||||||
|
resolvedMessageId = index;
|
||||||
|
message = chat[index];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!message?.is_user) return;
|
if (!message?.is_user) return;
|
||||||
runtime.recordRecallSentUserMessage(messageId, message.mes || "");
|
runtime.recordRecallSentUserMessage(
|
||||||
|
resolvedMessageId,
|
||||||
|
message.mes || "",
|
||||||
|
);
|
||||||
runtime.refreshPersistedRecallMessageUi?.();
|
runtime.refreshPersistedRecallMessageUi?.();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
14
index.js
14
index.js
@@ -8802,6 +8802,20 @@ function onGenerationStarted(type, params = {}, dryRun = false) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onGenerationEnded(_chatLength = null) {
|
function onGenerationEnded(_chatLength = null) {
|
||||||
|
const recentTransaction = findRecentGenerationRecallTransactionForChat();
|
||||||
|
const recentRecallResult =
|
||||||
|
getGenerationRecallTransactionResult(recentTransaction);
|
||||||
|
ensurePersistedRecallRecordForGeneration({
|
||||||
|
generationType: recentTransaction?.generationType || "normal",
|
||||||
|
recallResult: recentRecallResult,
|
||||||
|
transaction: recentTransaction,
|
||||||
|
recallOptions: recentTransaction?.frozenRecallOptions || null,
|
||||||
|
hookName:
|
||||||
|
recentRecallResult?.hookName ||
|
||||||
|
recentTransaction?.lastRecallMeta?.hookName ||
|
||||||
|
"",
|
||||||
|
});
|
||||||
|
schedulePersistedRecallMessageUiRefresh(320);
|
||||||
if (typeof scheduleMessageHideApply === "function") {
|
if (typeof scheduleMessageHideApply === "function") {
|
||||||
scheduleMessageHideApply("generation-ended", 180);
|
scheduleMessageHideApply("generation-ended", 180);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
onChatChangedController,
|
onChatChangedController,
|
||||||
onGenerationAfterCommandsController,
|
onGenerationAfterCommandsController,
|
||||||
onGenerationStartedController,
|
onGenerationStartedController,
|
||||||
|
onMessageSentController,
|
||||||
onMessageReceivedController,
|
onMessageReceivedController,
|
||||||
onMessageSwipedController,
|
onMessageSwipedController,
|
||||||
registerCoreEventHooksController,
|
registerCoreEventHooksController,
|
||||||
@@ -344,6 +345,7 @@ function createGenerationRecallHarness(options = {}) {
|
|||||||
moduleInjectionCalls: [],
|
moduleInjectionCalls: [],
|
||||||
recordedInjectionSnapshots: [],
|
recordedInjectionSnapshots: [],
|
||||||
refreshPanelCalls: 0,
|
refreshPanelCalls: 0,
|
||||||
|
hideScheduleCalls: [],
|
||||||
createRecallInputRecord,
|
createRecallInputRecord,
|
||||||
createRecallRunResult,
|
createRecallRunResult,
|
||||||
hashRecallInput,
|
hashRecallInput,
|
||||||
@@ -380,6 +382,7 @@ function createGenerationRecallHarness(options = {}) {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
getSettings: () => ({}),
|
getSettings: () => ({}),
|
||||||
|
$: () => ({}),
|
||||||
triggerChatMetadataSave: () => {
|
triggerChatMetadataSave: () => {
|
||||||
context.metadataSaveCalls += 1;
|
context.metadataSaveCalls += 1;
|
||||||
return "debounced";
|
return "debounced";
|
||||||
@@ -393,6 +396,11 @@ function createGenerationRecallHarness(options = {}) {
|
|||||||
schedulePersistedRecallMessageUiRefresh: () => {
|
schedulePersistedRecallMessageUiRefresh: () => {
|
||||||
context.recallUiRefreshCalls += 1;
|
context.recallUiRefreshCalls += 1;
|
||||||
},
|
},
|
||||||
|
getMessageHideSettings: () => ({}),
|
||||||
|
getHideRuntimeAdapters: () => ({}),
|
||||||
|
scheduleHideSettingsApply: (...args) => {
|
||||||
|
context.hideScheduleCalls.push(args);
|
||||||
|
},
|
||||||
estimateTokens: (text = "") =>
|
estimateTokens: (text = "") =>
|
||||||
normalizeRecallInputText(text)
|
normalizeRecallInputText(text)
|
||||||
.split(/\s+/)
|
.split(/\s+/)
|
||||||
@@ -414,7 +422,7 @@ function createGenerationRecallHarness(options = {}) {
|
|||||||
};
|
};
|
||||||
vm.createContext(context);
|
vm.createContext(context);
|
||||||
vm.runInContext(
|
vm.runInContext(
|
||||||
`${snippet}\nresult = { hashRecallInput, buildPreGenerationRecallKey, buildGenerationAfterCommandsRecallInput, cleanupGenerationRecallTransactions, buildGenerationRecallTransactionId, beginGenerationRecallTransaction, markGenerationRecallTransactionHookState, shouldRunRecallForTransaction, createGenerationRecallContext, onGenerationStarted, onGenerationAfterCommands, onBeforeCombinePrompts, applyFinalRecallInjectionForGeneration, generationRecallTransactions, freezeHostGenerationInputSnapshot, consumeHostGenerationInputSnapshot, getPendingHostGenerationInputSnapshot, recordRecallSendIntent, recordRecallSentUserMessage, getPendingRecallSendIntent: () => pendingRecallSendIntent, getLastRecallSentUserMessage: () => lastRecallSentUserMessage };`,
|
`${snippet}\nresult = { hashRecallInput, buildPreGenerationRecallKey, buildGenerationAfterCommandsRecallInput, cleanupGenerationRecallTransactions, buildGenerationRecallTransactionId, beginGenerationRecallTransaction, markGenerationRecallTransactionHookState, shouldRunRecallForTransaction, createGenerationRecallContext, onGenerationStarted, onGenerationEnded, onGenerationAfterCommands, onBeforeCombinePrompts, applyFinalRecallInjectionForGeneration, ensurePersistedRecallRecordForGeneration, findRecentGenerationRecallTransactionForChat, getGenerationRecallTransactionResult, generationRecallTransactions, freezeHostGenerationInputSnapshot, consumeHostGenerationInputSnapshot, getPendingHostGenerationInputSnapshot, recordRecallSendIntent, recordRecallSentUserMessage, getPendingRecallSendIntent: () => pendingRecallSendIntent, getLastRecallSentUserMessage: () => lastRecallSentUserMessage };`,
|
||||||
context,
|
context,
|
||||||
{ filename: indexPath },
|
{ filename: indexPath },
|
||||||
);
|
);
|
||||||
@@ -3143,6 +3151,39 @@ async function testSwipeRoutesToRerollWithoutHistoryRecoveryFallback() {
|
|||||||
assert.equal(result.recoveryPath, "reverse-journal");
|
assert.equal(result.recoveryPath, "reverse-journal");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function testMessageSentFallsBackToLatestUserWhenHostMessageIdInvalid() {
|
||||||
|
const recorded = [];
|
||||||
|
let refreshCalls = 0;
|
||||||
|
|
||||||
|
onMessageSentController(
|
||||||
|
{
|
||||||
|
getContext: () => ({
|
||||||
|
chat: [
|
||||||
|
{ is_user: true, mes: "较早用户楼层" },
|
||||||
|
{ is_user: false, mes: "assistant-tail" },
|
||||||
|
{ is_user: true, mes: "最新用户楼层" },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
recordRecallSentUserMessage(messageId, text, source = "message-sent") {
|
||||||
|
recorded.push({ messageId, text, source });
|
||||||
|
},
|
||||||
|
refreshPersistedRecallMessageUi() {
|
||||||
|
refreshCalls += 1;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.deepEqual(recorded, [
|
||||||
|
{
|
||||||
|
messageId: 2,
|
||||||
|
text: "最新用户楼层",
|
||||||
|
source: "message-sent",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
assert.equal(refreshCalls, 1);
|
||||||
|
}
|
||||||
|
|
||||||
async function testMessageReceivedQueuesExtractionWithoutRuntimeQueueMicrotask() {
|
async function testMessageReceivedQueuesExtractionWithoutRuntimeQueueMicrotask() {
|
||||||
let runExtractionCalls = 0;
|
let runExtractionCalls = 0;
|
||||||
let refreshCalls = 0;
|
let refreshCalls = 0;
|
||||||
@@ -3825,6 +3866,50 @@ async function testGenerationRecallImmediateAfterCommandsBackfillsPersistedRecor
|
|||||||
assert.equal(harness.metadataSaveCalls > 0, true);
|
assert.equal(harness.metadataSaveCalls > 0, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function testGenerationEndedBackfillsRecentRecallAndSchedulesHideRefresh() {
|
||||||
|
const harness = await createGenerationRecallHarness({ realApplyFinal: true });
|
||||||
|
harness.chat = [{ is_user: true, mes: "生成结束后补写目标" }];
|
||||||
|
const transaction = harness.result.beginGenerationRecallTransaction({
|
||||||
|
chatId: "chat-main",
|
||||||
|
generationType: "normal",
|
||||||
|
recallKey: "chat-main:normal:test-generation-ended",
|
||||||
|
forceNew: true,
|
||||||
|
});
|
||||||
|
transaction.frozenRecallOptions = {
|
||||||
|
generationType: "normal",
|
||||||
|
targetUserMessageIndex: null,
|
||||||
|
overrideUserMessage: "生成结束后补写目标",
|
||||||
|
lockedSource: "send-intent",
|
||||||
|
hookName: "GENERATION_AFTER_COMMANDS",
|
||||||
|
};
|
||||||
|
harness.result.generationRecallTransactions.set(transaction.id, transaction);
|
||||||
|
harness.result.markGenerationRecallTransactionHookState(
|
||||||
|
transaction,
|
||||||
|
"GENERATION_AFTER_COMMANDS",
|
||||||
|
"completed",
|
||||||
|
);
|
||||||
|
harness.result.getGenerationRecallTransactionResult(transaction);
|
||||||
|
transaction.lastRecallResult = {
|
||||||
|
status: "completed",
|
||||||
|
didRecall: true,
|
||||||
|
injectionText: "generation-ended-memory",
|
||||||
|
selectedNodeIds: ["node-z"],
|
||||||
|
sourceCandidates: [{ text: "生成结束后补写目标" }],
|
||||||
|
hookName: "GENERATION_AFTER_COMMANDS",
|
||||||
|
};
|
||||||
|
transaction.updatedAt = Date.now();
|
||||||
|
harness.result.generationRecallTransactions.set(transaction.id, transaction);
|
||||||
|
|
||||||
|
harness.result.onGenerationEnded();
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
harness.chat[0]?.extra?.bme_recall?.injectionText,
|
||||||
|
"generation-ended-memory",
|
||||||
|
);
|
||||||
|
assert.equal(harness.hideScheduleCalls.length, 1);
|
||||||
|
assert.equal(harness.hideScheduleCalls[0]?.[2], 180);
|
||||||
|
}
|
||||||
|
|
||||||
async function testRecallSubGraphAndDataLayerEntryPoints() {
|
async function testRecallSubGraphAndDataLayerEntryPoints() {
|
||||||
// Sub-graph build test (pure function, no DOM needed)
|
// Sub-graph build test (pure function, no DOM needed)
|
||||||
const { buildRecallSubGraph } = await import("../recall-message-ui.js");
|
const { buildRecallSubGraph } = await import("../recall-message-ui.js");
|
||||||
@@ -4624,6 +4709,7 @@ await testGenerationRecallSentMessageClearsStaleTransactionForSameKey();
|
|||||||
await testRegisterCoreEventHooksIsIdempotent();
|
await testRegisterCoreEventHooksIsIdempotent();
|
||||||
await testChatChangedDoesNotClearCoreEventBindings();
|
await testChatChangedDoesNotClearCoreEventBindings();
|
||||||
await testSwipeRoutesToRerollWithoutHistoryRecoveryFallback();
|
await testSwipeRoutesToRerollWithoutHistoryRecoveryFallback();
|
||||||
|
await testMessageSentFallsBackToLatestUserWhenHostMessageIdInvalid();
|
||||||
await testMessageReceivedQueuesExtractionWithoutRuntimeQueueMicrotask();
|
await testMessageReceivedQueuesExtractionWithoutRuntimeQueueMicrotask();
|
||||||
await testAutoExtractionDefersWhenGraphNotReady();
|
await testAutoExtractionDefersWhenGraphNotReady();
|
||||||
await testAutoExtractionDefersWhenAlreadyExtracting();
|
await testAutoExtractionDefersWhenAlreadyExtracting();
|
||||||
@@ -4636,6 +4722,7 @@ await testPersistentRecallSourceResolutionAndTargetRouting();
|
|||||||
await testGenerationRecallFinalInjectionRebindsLatestMatchingUserFloor();
|
await testGenerationRecallFinalInjectionRebindsLatestMatchingUserFloor();
|
||||||
await testGenerationRecallFinalInjectionBackfillsPersistedRecord();
|
await testGenerationRecallFinalInjectionBackfillsPersistedRecord();
|
||||||
await testGenerationRecallImmediateAfterCommandsBackfillsPersistedRecord();
|
await testGenerationRecallImmediateAfterCommandsBackfillsPersistedRecord();
|
||||||
|
await testGenerationEndedBackfillsRecentRecallAndSchedulesHideRefresh();
|
||||||
await testRecallCardMountsOnStandardUserMessageDom();
|
await testRecallCardMountsOnStandardUserMessageDom();
|
||||||
await testRecallCardSkipsMountWithoutStableMessageIndex();
|
await testRecallCardSkipsMountWithoutStableMessageIndex();
|
||||||
await testRecallCardDelayedDomInsertionEventuallyRenders();
|
await testRecallCardDelayedDomInsertionEventuallyRenders();
|
||||||
|
|||||||
Reference in New Issue
Block a user