mirror of
https://github.com/Youzini-afk/ST-Bionic-Memory-Ecology.git
synced 2026-05-15 22:30:38 +08:00
fix: finalize deepfix p2 recall binding and p3 validation matrix
This commit is contained in:
@@ -200,13 +200,14 @@ export async function onGenerationAfterCommandsController(
|
||||
return;
|
||||
}
|
||||
|
||||
const runtimeRecallOptions = recallContext.recallOptions || recallOptions || {};
|
||||
runtime.markGenerationRecallTransactionHookState(
|
||||
recallContext.transaction,
|
||||
recallContext.hookName,
|
||||
"running",
|
||||
);
|
||||
const recallResult = await runtime.runRecall({
|
||||
...recallOptions,
|
||||
...runtimeRecallOptions,
|
||||
recallKey: recallContext.recallKey,
|
||||
hookName: recallContext.hookName,
|
||||
signal: params?.signal,
|
||||
@@ -240,13 +241,14 @@ export async function onBeforeCombinePromptsController(runtime) {
|
||||
return;
|
||||
}
|
||||
|
||||
const runtimeRecallOptions = recallContext.recallOptions || recallOptions || {};
|
||||
runtime.markGenerationRecallTransactionHookState(
|
||||
recallContext.transaction,
|
||||
recallContext.hookName,
|
||||
"running",
|
||||
);
|
||||
const recallResult = await runtime.runRecall({
|
||||
...recallOptions,
|
||||
...runtimeRecallOptions,
|
||||
recallKey: recallContext.recallKey,
|
||||
hookName: recallContext.hookName,
|
||||
});
|
||||
|
||||
272
index.js
272
index.js
@@ -483,6 +483,7 @@ const PERSISTED_RECALL_UI_DIAGNOSTIC_THROTTLE_MS = 1500;
|
||||
const persistedRecallUiDiagnosticTimestamps = new Map();
|
||||
const persistedRecallPersistDiagnosticTimestamps = new Map();
|
||||
const GENERATION_RECALL_TRANSACTION_TTL_MS = 15000;
|
||||
const GENERATION_RECALL_HOOK_BRIDGE_MS = 1200;
|
||||
const stageNoticeHandles = {
|
||||
extraction: null,
|
||||
vector: null,
|
||||
@@ -4686,10 +4687,12 @@ function buildPreGenerationRecallKey(type, options = {}) {
|
||||
const seedText =
|
||||
options.overrideUserMessage || options.userMessage || `@target:${targetUserMessageIndex}`;
|
||||
|
||||
const normalizedChatId = normalizeChatIdCandidate(options.chatId || getCurrentChatId());
|
||||
|
||||
return [
|
||||
getCurrentChatId(),
|
||||
normalizedChatId,
|
||||
String(type || "normal").trim() || "normal",
|
||||
hashRecallInput(seedText),
|
||||
hashRecallInput(seedText || ""),
|
||||
].join(":");
|
||||
}
|
||||
|
||||
@@ -4707,6 +4710,90 @@ function cleanupGenerationRecallTransactions(now = Date.now()) {
|
||||
}
|
||||
}
|
||||
|
||||
function getGenerationRecallPeerHookName(hookName = "") {
|
||||
const normalized = String(hookName || "").trim();
|
||||
if (normalized === "GENERATION_AFTER_COMMANDS") {
|
||||
return "GENERATE_BEFORE_COMBINE_PROMPTS";
|
||||
}
|
||||
if (normalized === "GENERATE_BEFORE_COMBINE_PROMPTS") {
|
||||
return "GENERATION_AFTER_COMMANDS";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function isGenerationRecallTransactionWithinBridgeWindow(
|
||||
transaction,
|
||||
now = Date.now(),
|
||||
) {
|
||||
if (!transaction) return false;
|
||||
return now - Number(transaction.updatedAt || transaction.createdAt || 0) <= GENERATION_RECALL_HOOK_BRIDGE_MS;
|
||||
}
|
||||
|
||||
function normalizeGenerationRecallTransactionType(generationType = "normal") {
|
||||
const normalized = String(generationType || "normal").trim() || "normal";
|
||||
return normalized === "normal" ? "normal" : "history";
|
||||
}
|
||||
|
||||
function freezeGenerationRecallOptionsForTransaction(
|
||||
chat,
|
||||
generationType = "normal",
|
||||
recallOptions = {},
|
||||
) {
|
||||
if (!Array.isArray(chat)) return null;
|
||||
|
||||
const optionGenerationType = String(
|
||||
recallOptions?.generationType || generationType || "normal",
|
||||
).trim() || "normal";
|
||||
const normalizedGenerationType = optionGenerationType;
|
||||
|
||||
let targetUserMessageIndex = Number.isFinite(recallOptions?.targetUserMessageIndex)
|
||||
? Math.floor(Number(recallOptions.targetUserMessageIndex))
|
||||
: resolveGenerationTargetUserMessageIndex(chat, {
|
||||
generationType: normalizedGenerationType,
|
||||
});
|
||||
|
||||
if (!Number.isFinite(targetUserMessageIndex)) {
|
||||
return null;
|
||||
}
|
||||
targetUserMessageIndex = Math.floor(targetUserMessageIndex);
|
||||
|
||||
const targetUserMessage = chat[targetUserMessageIndex];
|
||||
if (!targetUserMessage?.is_user) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const frozenUserMessage = normalizeRecallInputText(
|
||||
targetUserMessage?.mes ||
|
||||
recallOptions?.overrideUserMessage ||
|
||||
recallOptions?.userMessage ||
|
||||
"",
|
||||
);
|
||||
if (!frozenUserMessage) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const source =
|
||||
String(recallOptions?.overrideSource || recallOptions?.source || "").trim() ||
|
||||
(normalizeGenerationRecallTransactionType(normalizedGenerationType) === "normal"
|
||||
? "chat-tail-user"
|
||||
: "chat-last-user");
|
||||
const sourceLabel =
|
||||
String(
|
||||
recallOptions?.overrideSourceLabel ||
|
||||
recallOptions?.sourceLabel ||
|
||||
getRecallUserMessageSourceLabel(source),
|
||||
).trim() || getRecallUserMessageSourceLabel(source);
|
||||
|
||||
return {
|
||||
generationType: normalizedGenerationType,
|
||||
targetUserMessageIndex,
|
||||
overrideUserMessage: frozenUserMessage,
|
||||
overrideSource: source,
|
||||
overrideSourceLabel: sourceLabel,
|
||||
includeSyntheticUserMessage: false,
|
||||
};
|
||||
}
|
||||
|
||||
function buildGenerationRecallTransactionId(chatId, generationType, recallKey) {
|
||||
return [
|
||||
String(chatId || ""),
|
||||
@@ -4719,6 +4806,7 @@ function beginGenerationRecallTransaction({
|
||||
chatId,
|
||||
generationType = "normal",
|
||||
recallKey = "",
|
||||
forceNew = false,
|
||||
} = {}) {
|
||||
const normalizedChatId = String(chatId || "");
|
||||
const normalizedGenerationType =
|
||||
@@ -4732,20 +4820,98 @@ function beginGenerationRecallTransaction({
|
||||
normalizedGenerationType,
|
||||
normalizedRecallKey,
|
||||
);
|
||||
|
||||
const now = Date.now();
|
||||
const transaction = generationRecallTransactions.get(transactionId) || {
|
||||
const existingTransaction = generationRecallTransactions.get(transactionId) || null;
|
||||
if (
|
||||
existingTransaction &&
|
||||
isGenerationRecallTransactionWithinBridgeWindow(existingTransaction, now) &&
|
||||
!forceNew
|
||||
) {
|
||||
existingTransaction.updatedAt = now;
|
||||
generationRecallTransactions.set(transactionId, existingTransaction);
|
||||
return existingTransaction;
|
||||
}
|
||||
|
||||
const transaction = {
|
||||
id: transactionId,
|
||||
chatId: normalizedChatId,
|
||||
generationType: normalizedGenerationType,
|
||||
recallKey: normalizedRecallKey,
|
||||
hookStates: {},
|
||||
createdAt: now,
|
||||
frozenRecallOptions: null,
|
||||
};
|
||||
transaction.updatedAt = now;
|
||||
generationRecallTransactions.set(transactionId, transaction);
|
||||
return transaction;
|
||||
}
|
||||
|
||||
function findRecentGenerationRecallTransactionForChat(
|
||||
chatId = getCurrentChatId(),
|
||||
now = Date.now(),
|
||||
) {
|
||||
const normalizedChatId = normalizeChatIdCandidate(chatId);
|
||||
if (!normalizedChatId) return null;
|
||||
|
||||
let latestTransaction = null;
|
||||
for (const transaction of generationRecallTransactions.values()) {
|
||||
if (!transaction || String(transaction.chatId || "") !== normalizedChatId) continue;
|
||||
if (!isGenerationRecallTransactionWithinBridgeWindow(transaction, now)) continue;
|
||||
if (!latestTransaction || Number(transaction.updatedAt || 0) > Number(latestTransaction.updatedAt || 0)) {
|
||||
latestTransaction = transaction;
|
||||
}
|
||||
}
|
||||
|
||||
return latestTransaction;
|
||||
}
|
||||
|
||||
function shouldReuseRecentGenerationRecallTransaction(
|
||||
transaction,
|
||||
hookName,
|
||||
recallKey = "",
|
||||
now = Date.now(),
|
||||
) {
|
||||
if (!transaction || !hookName) return false;
|
||||
if (!isGenerationRecallTransactionWithinBridgeWindow(transaction, now)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const hookStates = transaction.hookStates || {};
|
||||
const normalizedRecallKey = String(recallKey || "");
|
||||
const transactionRecallKey = String(transaction.recallKey || "");
|
||||
|
||||
if (Object.values(hookStates).includes("running")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const peerHookName = getGenerationRecallPeerHookName(hookName);
|
||||
const peerHookState = peerHookName ? hookStates[peerHookName] : "";
|
||||
if (peerHookState) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const ownState = hookStates[hookName];
|
||||
if (ownState) {
|
||||
return ownState === "running";
|
||||
}
|
||||
|
||||
if (!Object.keys(hookStates).length) {
|
||||
if (!transactionRecallKey) {
|
||||
return true;
|
||||
}
|
||||
if (!normalizedRecallKey) {
|
||||
return false;
|
||||
}
|
||||
if (normalizedRecallKey !== transactionRecallKey) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function markGenerationRecallTransactionHookState(
|
||||
transaction,
|
||||
hookName,
|
||||
@@ -4820,20 +4986,102 @@ function createGenerationRecallContext({
|
||||
recallOptions = {},
|
||||
chatId = getCurrentChatId(),
|
||||
} = {}) {
|
||||
const recallKey =
|
||||
recallOptions.recallKey ||
|
||||
buildPreGenerationRecallKey(generationType, recallOptions);
|
||||
const transaction = beginGenerationRecallTransaction({
|
||||
chatId,
|
||||
const context = getContext();
|
||||
const chat = context?.chat;
|
||||
const normalizedChatId = normalizeChatIdCandidate(
|
||||
chatId || context?.chatId || getCurrentChatId(),
|
||||
);
|
||||
|
||||
const frozenRecallOptions = freezeGenerationRecallOptionsForTransaction(
|
||||
chat,
|
||||
generationType,
|
||||
recallKey,
|
||||
});
|
||||
recallOptions,
|
||||
);
|
||||
if (!frozenRecallOptions) {
|
||||
return {
|
||||
hookName,
|
||||
generationType,
|
||||
recallKey: "",
|
||||
transaction: null,
|
||||
recallOptions: null,
|
||||
shouldRun: false,
|
||||
};
|
||||
}
|
||||
|
||||
const transactionGenerationType = normalizeGenerationRecallTransactionType(
|
||||
frozenRecallOptions.generationType || generationType,
|
||||
);
|
||||
const fallbackRecallKey =
|
||||
recallOptions.recallKey ||
|
||||
buildPreGenerationRecallKey(transactionGenerationType, {
|
||||
...frozenRecallOptions,
|
||||
chatId: normalizedChatId,
|
||||
userMessage: frozenRecallOptions.overrideUserMessage,
|
||||
});
|
||||
|
||||
const now = Date.now();
|
||||
const recentTransaction = findRecentGenerationRecallTransactionForChat(
|
||||
normalizedChatId,
|
||||
now,
|
||||
);
|
||||
let transaction = recentTransaction;
|
||||
if (
|
||||
!shouldReuseRecentGenerationRecallTransaction(
|
||||
transaction,
|
||||
hookName,
|
||||
fallbackRecallKey,
|
||||
now,
|
||||
)
|
||||
) {
|
||||
transaction = beginGenerationRecallTransaction({
|
||||
chatId: normalizedChatId,
|
||||
generationType: transactionGenerationType,
|
||||
recallKey: fallbackRecallKey,
|
||||
forceNew: true,
|
||||
});
|
||||
}
|
||||
|
||||
if (!transaction) {
|
||||
return {
|
||||
hookName,
|
||||
generationType,
|
||||
recallKey: "",
|
||||
transaction: null,
|
||||
recallOptions: null,
|
||||
shouldRun: false,
|
||||
};
|
||||
}
|
||||
|
||||
if (!transaction.frozenRecallOptions || typeof transaction.frozenRecallOptions !== "object") {
|
||||
transaction.frozenRecallOptions = {
|
||||
...frozenRecallOptions,
|
||||
};
|
||||
}
|
||||
if (!String(transaction.recallKey || "").trim()) {
|
||||
transaction.recallKey = fallbackRecallKey;
|
||||
}
|
||||
if (!String(transaction.generationType || "").trim()) {
|
||||
transaction.generationType = transactionGenerationType;
|
||||
}
|
||||
transaction.updatedAt = now;
|
||||
generationRecallTransactions.set(transaction.id, transaction);
|
||||
|
||||
const boundRecallOptions = {
|
||||
...(transaction.frozenRecallOptions || frozenRecallOptions),
|
||||
recallKey: transaction.recallKey,
|
||||
generationType: transaction.frozenRecallOptions?.generationType || generationType,
|
||||
};
|
||||
|
||||
const recallKey = String(transaction.recallKey || fallbackRecallKey || "");
|
||||
const shouldRun = shouldRunRecallForTransaction(transaction, hookName);
|
||||
|
||||
return {
|
||||
hookName,
|
||||
generationType,
|
||||
generationType: boundRecallOptions.generationType,
|
||||
recallKey,
|
||||
transaction,
|
||||
shouldRun: shouldRunRecallForTransaction(transaction, hookName),
|
||||
recallOptions: boundRecallOptions,
|
||||
shouldRun,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
{
|
||||
"scripts": {
|
||||
"test:p0": "node tests/p0-regressions.mjs",
|
||||
"test:runtime-history": "node tests/runtime-history.mjs",
|
||||
"test:graph-persistence": "node tests/graph-persistence.mjs",
|
||||
"test:indexeddb-persistence": "node tests/indexeddb-persistence.mjs",
|
||||
"test:indexeddb-sync": "node tests/indexeddb-sync.mjs",
|
||||
"test:indexeddb-migration": "node tests/indexeddb-migration.mjs",
|
||||
"test:indexeddb": "npm run test:indexeddb-persistence && npm run test:indexeddb-sync && npm run test:indexeddb-migration",
|
||||
"test:all": "npm run test:p0 && npm run test:graph-persistence && npm run test:indexeddb",
|
||||
"test:persistence-matrix": "npm run test:p0 && npm run test:runtime-history && npm run test:graph-persistence && npm run test:indexeddb",
|
||||
"test:all": "npm run test:persistence-matrix",
|
||||
"check": "node --check index.js && node --check bme-db.js && node --check panel.js && node --check ui-status.js && node --check event-binding.js"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@@ -882,6 +882,86 @@ result = {
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
const harness = await createGraphPersistenceHarness({
|
||||
chatId: "chat-sync-refresh-merge",
|
||||
chatMetadata: {
|
||||
integrity: "chat-sync-refresh-merge-ready",
|
||||
},
|
||||
});
|
||||
harness.api.setCurrentGraph(
|
||||
normalizeGraphRuntimeState(
|
||||
createMeaningfulGraph("chat-sync-refresh-merge", "stale-runtime-merge"),
|
||||
"chat-sync-refresh-merge",
|
||||
),
|
||||
);
|
||||
harness.api.setGraphPersistenceState({
|
||||
loadState: "loaded",
|
||||
chatId: "chat-sync-refresh-merge",
|
||||
reason: "runtime-stale",
|
||||
revision: 3,
|
||||
lastPersistedRevision: 3,
|
||||
dbReady: true,
|
||||
writesBlocked: false,
|
||||
});
|
||||
harness.api.setIndexedDbSnapshot(
|
||||
buildSnapshotFromGraph(
|
||||
createMeaningfulGraph("chat-sync-refresh-merge", "fresh-indexeddb-merge"),
|
||||
{
|
||||
chatId: "chat-sync-refresh-merge",
|
||||
revision: 8,
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
const runtimeOptions = harness.api.buildBmeSyncRuntimeOptions();
|
||||
await runtimeOptions.onSyncApplied({
|
||||
chatId: "chat-sync-refresh-merge",
|
||||
action: "merge",
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
harness.api.getCurrentGraph().nodes[0]?.fields?.title,
|
||||
"事件-fresh-indexeddb-merge",
|
||||
"merge 后应刷新当前运行时图谱",
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
const harness = await createGraphPersistenceHarness({
|
||||
chatId: "chat-sync-refresh-active",
|
||||
chatMetadata: {
|
||||
integrity: "chat-sync-refresh-active-ready",
|
||||
},
|
||||
});
|
||||
harness.api.setCurrentGraph(
|
||||
normalizeGraphRuntimeState(
|
||||
createMeaningfulGraph("chat-sync-refresh-active", "active-runtime"),
|
||||
"chat-sync-refresh-active",
|
||||
),
|
||||
);
|
||||
harness.api.setGraphPersistenceState({
|
||||
loadState: "loaded",
|
||||
chatId: "chat-sync-refresh-active",
|
||||
reason: "runtime-active",
|
||||
revision: 4,
|
||||
dbReady: true,
|
||||
writesBlocked: false,
|
||||
});
|
||||
|
||||
const runtimeOptions = harness.api.buildBmeSyncRuntimeOptions();
|
||||
await runtimeOptions.onSyncApplied({
|
||||
chatId: "chat-sync-refresh-other",
|
||||
action: "download",
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
harness.api.getCurrentGraph().nodes[0]?.fields?.title,
|
||||
"事件-active-runtime",
|
||||
"active chat 与 sync payload chat 不一致时不应覆盖当前运行时图谱",
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
const sharedSession = new Map();
|
||||
const writer = await createGraphPersistenceHarness({
|
||||
|
||||
@@ -712,7 +712,12 @@ async function testSyncAppliedHook() {
|
||||
const mergeResult = await syncNow("chat-hook-merge", runtime);
|
||||
assert.equal(mergeResult.action, "merge");
|
||||
|
||||
assert.equal(downloadResult.revision, 3);
|
||||
assert.equal(mergeResult.revision, 5);
|
||||
|
||||
assert.deepEqual(hookCalls.map((item) => item.action), ["download", "merge"]);
|
||||
assert.deepEqual(hookCalls.map((item) => item.chatId), ["chat-hook-download", "chat-hook-merge"]);
|
||||
assert.deepEqual(hookCalls.map((item) => item.revision), [3, 5]);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
|
||||
@@ -1929,6 +1929,91 @@ async function testGenerationRecallTransactionDedupesDoubleHookBySameKey() {
|
||||
assert.equal(harness.runRecallCalls[0].hookName, "GENERATION_AFTER_COMMANDS");
|
||||
}
|
||||
|
||||
async function testGenerationRecallTransactionDedupesReverseHookOrder() {
|
||||
const harness = await createGenerationRecallHarness();
|
||||
harness.chat = [{ is_user: true, mes: "逆序同轮输入" }];
|
||||
|
||||
await harness.result.onBeforeCombinePrompts();
|
||||
await harness.result.onGenerationAfterCommands("normal", {}, false);
|
||||
|
||||
assert.equal(harness.runRecallCalls.length, 1);
|
||||
assert.equal(
|
||||
harness.runRecallCalls[0].hookName,
|
||||
"GENERATE_BEFORE_COMBINE_PROMPTS",
|
||||
);
|
||||
}
|
||||
|
||||
async function testGenerationRecallHistoryModesUseSameBindingAcrossHooks() {
|
||||
for (const generationType of ["continue", "regenerate", "swipe"]) {
|
||||
const harness = await createGenerationRecallHarness();
|
||||
const userMessage = `历史输入-${generationType}`;
|
||||
harness.chat = [
|
||||
{ is_user: true, mes: userMessage },
|
||||
{ is_user: false, mes: "assistant-tail" },
|
||||
];
|
||||
|
||||
await harness.result.onGenerationAfterCommands(generationType, {}, false);
|
||||
await harness.result.onBeforeCombinePrompts();
|
||||
|
||||
assert.equal(harness.runRecallCalls.length, 1, `${generationType} 应只执行一次召回`);
|
||||
assert.equal(harness.runRecallCalls[0].hookName, "GENERATION_AFTER_COMMANDS");
|
||||
assert.equal(harness.runRecallCalls[0].targetUserMessageIndex, 0);
|
||||
assert.equal(harness.runRecallCalls[0].overrideUserMessage, userMessage);
|
||||
}
|
||||
}
|
||||
|
||||
async function testGenerationRecallFrozenBindingSurvivesCrossHookInputDrift() {
|
||||
const harness = await createGenerationRecallHarness();
|
||||
harness.chat = [{ is_user: true, mes: "稳定输入-A" }];
|
||||
|
||||
await harness.result.onGenerationAfterCommands("normal", {}, false);
|
||||
harness.chat = [{ is_user: true, mes: "稳定输入-B" }];
|
||||
await harness.result.onBeforeCombinePrompts();
|
||||
|
||||
assert.equal(harness.runRecallCalls.length, 1);
|
||||
assert.equal(harness.runRecallCalls[0].overrideUserMessage, "稳定输入-A");
|
||||
}
|
||||
|
||||
async function testGenerationRecallSkipsUntilTargetUserFloorAvailable() {
|
||||
const harness = await createGenerationRecallHarness();
|
||||
harness.chat = [{ is_user: false, mes: "assistant-only" }];
|
||||
|
||||
await harness.result.onGenerationAfterCommands("normal", {}, false);
|
||||
assert.equal(harness.runRecallCalls.length, 0);
|
||||
|
||||
harness.chat = [{ is_user: true, mes: "补齐 user 楼层" }];
|
||||
await harness.result.onBeforeCombinePrompts();
|
||||
assert.equal(harness.runRecallCalls.length, 1);
|
||||
assert.equal(
|
||||
harness.runRecallCalls[0].hookName,
|
||||
"GENERATE_BEFORE_COMBINE_PROMPTS",
|
||||
);
|
||||
}
|
||||
|
||||
async function testGenerationRecallSameKeyCanRunAgainImmediatelyAsNewGeneration() {
|
||||
const harness = await createGenerationRecallHarness();
|
||||
harness.chat = [{ is_user: true, mes: "同 key 连续生成" }];
|
||||
|
||||
await harness.result.onGenerationAfterCommands("normal", {}, false);
|
||||
await harness.result.onGenerationAfterCommands("normal", {}, false);
|
||||
|
||||
assert.equal(harness.runRecallCalls.length, 2);
|
||||
assert.equal(harness.runRecallCalls[0].recallKey, harness.runRecallCalls[1].recallKey);
|
||||
}
|
||||
|
||||
async function testGenerationRecallSameKeyCanRunAgainAfterBridgeWindow() {
|
||||
const harness = await createGenerationRecallHarness();
|
||||
harness.chat = [{ is_user: true, mes: "同 key 重复生成" }];
|
||||
|
||||
await harness.result.onGenerationAfterCommands("normal", {}, false);
|
||||
const transaction = [...harness.result.generationRecallTransactions.values()][0];
|
||||
transaction.updatedAt = Date.now() - 5000;
|
||||
harness.result.generationRecallTransactions.set(transaction.id, transaction);
|
||||
await harness.result.onGenerationAfterCommands("normal", {}, false);
|
||||
|
||||
assert.equal(harness.runRecallCalls.length, 2);
|
||||
}
|
||||
|
||||
async function testGenerationRecallBeforeCombineRunsStandalone() {
|
||||
const harness = await createGenerationRecallHarness();
|
||||
harness.chat = [{ is_user: true, mes: "仅 before combine" }];
|
||||
@@ -2471,6 +2556,12 @@ await testBatchStatusSemanticFailureDoesNotHideCoreSuccess();
|
||||
await testBatchStatusFinalizeFailureIsNotCompleteSuccess();
|
||||
await testProcessedHistoryAdvanceRequiresCompleteStrongSuccess();
|
||||
await testGenerationRecallTransactionDedupesDoubleHookBySameKey();
|
||||
await testGenerationRecallTransactionDedupesReverseHookOrder();
|
||||
await testGenerationRecallHistoryModesUseSameBindingAcrossHooks();
|
||||
await testGenerationRecallFrozenBindingSurvivesCrossHookInputDrift();
|
||||
await testGenerationRecallSkipsUntilTargetUserFloorAvailable();
|
||||
await testGenerationRecallSameKeyCanRunAgainImmediatelyAsNewGeneration();
|
||||
await testGenerationRecallSameKeyCanRunAgainAfterBridgeWindow();
|
||||
await testGenerationRecallBeforeCombineRunsStandalone();
|
||||
await testGenerationRecallDifferentKeyCanRunAgain();
|
||||
await testGenerationRecallSkippedStateDoesNotLoopToBeforeCombine();
|
||||
|
||||
27
ui-status.js
27
ui-status.js
@@ -278,16 +278,33 @@ export function isTerminalGenerationRecallHookState(state = "") {
|
||||
export function shouldRunRecallForTransaction(transaction, hookName) {
|
||||
if (!hookName) return true;
|
||||
if (!transaction) return true;
|
||||
|
||||
const hookStates = transaction.hookStates || {};
|
||||
if (isTerminalGenerationRecallHookState(hookStates[hookName])) {
|
||||
return false;
|
||||
}
|
||||
const currentHookState = hookStates[hookName];
|
||||
if (
|
||||
hookName === "GENERATE_BEFORE_COMBINE_PROMPTS" &&
|
||||
isTerminalGenerationRecallHookState(hookStates.GENERATION_AFTER_COMMANDS)
|
||||
currentHookState === "running" ||
|
||||
isTerminalGenerationRecallHookState(currentHookState)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const peerHookName =
|
||||
hookName === "GENERATION_AFTER_COMMANDS"
|
||||
? "GENERATE_BEFORE_COMBINE_PROMPTS"
|
||||
: hookName === "GENERATE_BEFORE_COMBINE_PROMPTS"
|
||||
? "GENERATION_AFTER_COMMANDS"
|
||||
: "";
|
||||
|
||||
if (!peerHookName) return true;
|
||||
|
||||
const peerHookState = hookStates[peerHookName];
|
||||
if (
|
||||
peerHookState === "running" ||
|
||||
isTerminalGenerationRecallHookState(peerHookState)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user