mirror of
https://github.com/Youzini-afk/ST-Bionic-Memory-Ecology.git
synced 2026-05-15 22:30:38 +08:00
422 lines
16 KiB
JavaScript
422 lines
16 KiB
JavaScript
import fs from "node:fs/promises";
|
|
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import vm from "node:vm";
|
|
import {
|
|
onBeforeCombinePromptsController,
|
|
onGenerationAfterCommandsController,
|
|
onGenerationStartedController,
|
|
onMessageReceivedController,
|
|
onMessageSentController,
|
|
} from "../../host/event-binding.js";
|
|
import { isSystemMessageForExtraction } from "../../maintenance/chat-history.js";
|
|
import { resolveAutoExtractionPlanController } from "../../maintenance/extraction-controller.js";
|
|
import {
|
|
GRAPH_LOAD_STATES,
|
|
GRAPH_METADATA_KEY,
|
|
GRAPH_PERSISTENCE_META_KEY,
|
|
MODULE_NAME,
|
|
} from "../../graph/graph-persistence.js";
|
|
import { getSmartTriggerDecision } from "../../maintenance/smart-trigger.js";
|
|
import {
|
|
buildPersistedRecallRecord,
|
|
bumpPersistedRecallGenerationCount,
|
|
readPersistedRecallFromUserMessage,
|
|
resolveFinalRecallInjectionSource,
|
|
writePersistedRecallToUserMessage,
|
|
} from "../../retrieval/recall-persistence.js";
|
|
import {
|
|
createGraphPersistenceState,
|
|
createRecallInputRecord,
|
|
createRecallRunResult,
|
|
createUiStatus,
|
|
getGenerationRecallHookStateFromResult,
|
|
getRecallHookLabel,
|
|
getStageNoticeDuration,
|
|
getStageNoticeTitle,
|
|
hashRecallInput,
|
|
isFreshRecallInputRecord,
|
|
isTerminalGenerationRecallHookState,
|
|
isTrivialUserInput,
|
|
normalizeRecallInputText,
|
|
normalizeStageNoticeLevel,
|
|
shouldRunRecallForTransaction,
|
|
} from "../../ui/ui-status.js";
|
|
import {
|
|
defaultSettings,
|
|
mergePersistedSettings,
|
|
} from "../../runtime/settings-defaults.js";
|
|
|
|
const moduleDir = path.dirname(fileURLToPath(import.meta.url));
|
|
const indexPath = path.resolve(moduleDir, "../../index.js");
|
|
|
|
export function createGenerationRecallHarness(options = {}) {
|
|
const { realApplyFinal = false } = options;
|
|
return fs.readFile(indexPath, "utf8").then((source) => {
|
|
const start = source.indexOf("const RECALL_INPUT_RECORD_TTL_MS = 60000;");
|
|
const end = source.indexOf(
|
|
'function onMessageReceived(messageId = null, type = "") {',
|
|
);
|
|
const endFallback = source.indexOf("async function runExtraction()");
|
|
const resolvedEnd = end >= 0 ? end : endFallback;
|
|
if (start < 0 || resolvedEnd < 0 || resolvedEnd <= start) {
|
|
throw new Error("无法从 index.js 提取生成召回事务定义");
|
|
}
|
|
const snippet = source
|
|
.slice(start, resolvedEnd)
|
|
.replace(/^export\s+/gm, "");
|
|
const context = {
|
|
console,
|
|
Date,
|
|
Map,
|
|
setTimeout,
|
|
clearTimeout,
|
|
__sendTextareaValue: "",
|
|
document: {
|
|
getElementById(id) {
|
|
if (
|
|
id === "send_textarea" &&
|
|
typeof context.__sendTextareaValue === "string" &&
|
|
context.__sendTextareaValue
|
|
) {
|
|
return { value: context.__sendTextareaValue };
|
|
}
|
|
return null;
|
|
},
|
|
},
|
|
result: null,
|
|
currentGraph: {},
|
|
_panelModule: null,
|
|
defaultSettings,
|
|
mergePersistedSettings,
|
|
settings: {},
|
|
graphPersistenceState: createGraphPersistenceState(),
|
|
extension_settings: { [MODULE_NAME]: {} },
|
|
extension_prompt_types: {
|
|
NONE: 0,
|
|
BEFORE_PROMPT: 1,
|
|
IN_PROMPT: 2,
|
|
IN_CHAT: 3,
|
|
},
|
|
extension_prompt_roles: {
|
|
SYSTEM: 0,
|
|
USER: 1,
|
|
ASSISTANT: 2,
|
|
},
|
|
clampInt: (value, fallback = 0, min = 0, max = 9999) => {
|
|
const numeric = Number(value);
|
|
if (!Number.isFinite(numeric)) return fallback;
|
|
return Math.min(max, Math.max(min, Math.trunc(numeric)));
|
|
},
|
|
getHostAdapter: () => null,
|
|
migrateLegacyTaskProfiles: (settings = {}) => ({
|
|
taskProfilesVersion: settings?.taskProfilesVersion || 0,
|
|
taskProfiles: settings?.taskProfiles || {},
|
|
}),
|
|
migratePerTaskRegexToGlobal: (settings = {}) => ({
|
|
changed: false,
|
|
settings,
|
|
}),
|
|
refreshPanelLiveStateController: () => {
|
|
context.refreshPanelCalls += 1;
|
|
},
|
|
isRecalling: false,
|
|
getCurrentChatId: () => "chat-main",
|
|
normalizeRecallInputText: (text = "") => String(text || "").trim(),
|
|
isTrivialUserInput,
|
|
getAssistantTurns: (chat = []) =>
|
|
chat.flatMap((message, index) =>
|
|
!message?.is_user &&
|
|
!isSystemMessageForExtraction(message, { index, chat })
|
|
? [index]
|
|
: [],
|
|
),
|
|
isSystemMessageForExtraction,
|
|
getLatestUserChatMessage: (chat = []) =>
|
|
[...chat].reverse().find((message) => message?.is_user) || null,
|
|
getLastNonSystemChatMessage: (chat = []) =>
|
|
[...chat]
|
|
.map((message, index) => ({ message, index }))
|
|
.reverse()
|
|
.find(
|
|
({ message, index }) =>
|
|
!isSystemMessageForExtraction(message, { index, chat }),
|
|
)?.message || null,
|
|
getSmartTriggerDecision,
|
|
getSendTextareaValue: () => context.__sendTextareaValue,
|
|
getRecallUserMessageSourceLabel: (source = "") => source,
|
|
getRecallUserMessageSourceLabelController: (source = "") => source,
|
|
buildRecallRecentMessages: (
|
|
chat = [],
|
|
_limit,
|
|
syntheticUserMessage = "",
|
|
) =>
|
|
syntheticUserMessage
|
|
? [...chat, { is_user: true, mes: syntheticUserMessage }]
|
|
: [...chat],
|
|
getContext: () => ({
|
|
chatId: "chat-main",
|
|
chat: context.chat,
|
|
}),
|
|
chat: [],
|
|
runRecallCalls: [],
|
|
runExtractionCalls: [],
|
|
extractionIssues: [],
|
|
applyFinalCalls: [],
|
|
moduleInjectionCalls: [],
|
|
recordedInjectionSnapshots: [],
|
|
refreshPanelCalls: 0,
|
|
hideScheduleCalls: [],
|
|
isExtracting: false,
|
|
isRecoveringHistory: false,
|
|
isRestoreLockActive: () => false,
|
|
isAssistantChatMessage: (message) =>
|
|
Boolean(message) && !message.is_user && !message.is_system,
|
|
createRecallInputRecord,
|
|
createRecallRunResult,
|
|
hashRecallInput,
|
|
isFreshRecallInputRecord,
|
|
isTerminalGenerationRecallHookState,
|
|
shouldRunRecallForTransaction,
|
|
getGenerationRecallHookStateFromResult,
|
|
createUiStatus,
|
|
createGraphPersistenceState,
|
|
getRecallHookLabel,
|
|
getStageNoticeTitle,
|
|
getStageNoticeDuration,
|
|
normalizeStageNoticeLevel,
|
|
MODULE_NAME,
|
|
GRAPH_LOAD_STATES,
|
|
GRAPH_METADATA_KEY,
|
|
GRAPH_PERSISTENCE_META_KEY,
|
|
resolveAutoExtractionPlanController,
|
|
onBeforeCombinePromptsController,
|
|
onGenerationAfterCommandsController,
|
|
onGenerationStartedController,
|
|
readPersistedRecallFromUserMessage,
|
|
writePersistedRecallToUserMessage,
|
|
buildPersistedRecallRecord,
|
|
resolveFinalRecallInjectionSource,
|
|
bumpPersistedRecallGenerationCount,
|
|
applyModuleInjectionPrompt: (text = "") => {
|
|
const normalizedText = String(text || "");
|
|
context.moduleInjectionCalls.push(normalizedText);
|
|
return {
|
|
applied: Boolean(normalizedText.trim()),
|
|
source: normalizedText.trim() ? "module-injection" : "rewrite-clear",
|
|
mode: normalizedText.trim() ? "module-injection" : "rewrite-clear",
|
|
};
|
|
},
|
|
getSettings: () => context.settings,
|
|
$: () => ({}),
|
|
triggerChatMetadataSave: () => {
|
|
context.metadataSaveCalls += 1;
|
|
return "debounced";
|
|
},
|
|
refreshPanelLiveState: () => {
|
|
context.refreshPanelCalls += 1;
|
|
},
|
|
recordInjectionSnapshot: (_kind, snapshot = {}) => {
|
|
context.recordedInjectionSnapshots.push({ ...snapshot });
|
|
},
|
|
schedulePersistedRecallMessageUiRefresh: () => {
|
|
context.recallUiRefreshCalls += 1;
|
|
},
|
|
getMessageHideSettings: () => ({}),
|
|
getHideRuntimeAdapters: () => ({}),
|
|
scheduleHideSettingsApply: (...args) => {
|
|
context.hideScheduleCalls.push(args);
|
|
},
|
|
estimateTokens: (text = "") =>
|
|
normalizeRecallInputText(text)
|
|
.split(/\s+/)
|
|
.filter(Boolean).length || (normalizeRecallInputText(text) ? 1 : 0),
|
|
resolveGenerationTargetUserMessageIndex: (
|
|
chat = [],
|
|
{ generationType } = {},
|
|
) => {
|
|
const normalized = String(generationType || "normal");
|
|
if (!Array.isArray(chat) || chat.length === 0) return null;
|
|
if (normalized === "normal")
|
|
return chat[chat.length - 1]?.is_user ? chat.length - 1 : null;
|
|
for (let index = chat.length - 1; index >= 0; index--)
|
|
if (chat[index]?.is_user) return index;
|
|
return null;
|
|
},
|
|
metadataSaveCalls: 0,
|
|
recallUiRefreshCalls: 0,
|
|
};
|
|
vm.createContext(context);
|
|
vm.runInContext(
|
|
`${snippet}\nresult = { hashRecallInput, buildPreGenerationRecallKey, buildGenerationAfterCommandsRecallInput, buildNormalGenerationRecallInput, cleanupGenerationRecallTransactions, buildGenerationRecallTransactionId, beginGenerationRecallTransaction, markGenerationRecallTransactionHookState, shouldRunRecallForTransaction, createGenerationRecallContext, onGenerationStarted, onGenerationEnded, onGenerationAfterCommands, onBeforeCombinePrompts, applyFinalRecallInjectionForGeneration, ensurePersistedRecallRecordForGeneration, findRecentGenerationRecallTransactionForChat, getGenerationRecallTransactionResult, generationRecallTransactions, freezeHostGenerationInputSnapshot, consumeHostGenerationInputSnapshot, getPendingHostGenerationInputSnapshot, clearPendingHostGenerationInputSnapshot, recordRecallSendIntent, clearPendingRecallSendIntent, recordRecallSentUserMessage, getPendingRecallSendIntent: () => pendingRecallSendIntent, getLastRecallSentUserMessage: () => lastRecallSentUserMessage, getCurrentGenerationTrivialSkip, markCurrentGenerationTrivialSkip, clearCurrentGenerationTrivialSkip, consumeCurrentGenerationTrivialSkip, deferAutoExtraction, maybeResumePendingAutoExtraction, clearPendingAutoExtraction, getPendingAutoExtraction: () => ({ ...pendingAutoExtraction }), getIsHostGenerationRunning: () => isHostGenerationRunning, preparePlannerRecallHandoff, runPlannerRecallForEna, getGraphPersistenceState: () => graphPersistenceState, setGraphPersistenceState: (value = {}) => { graphPersistenceState = { ...graphPersistenceState, ...(value || {}) }; return graphPersistenceState; } };`,
|
|
context,
|
|
{ filename: indexPath },
|
|
);
|
|
|
|
Object.defineProperties(context, {
|
|
pendingRecallSendIntent: {
|
|
get() {
|
|
return context.result.getPendingRecallSendIntent();
|
|
},
|
|
set(value) {
|
|
if (value?.text) {
|
|
context.result.recordRecallSendIntent(
|
|
value?.text || "",
|
|
value?.source,
|
|
);
|
|
return;
|
|
}
|
|
context.result.clearPendingRecallSendIntent();
|
|
},
|
|
configurable: true,
|
|
},
|
|
lastRecallSentUserMessage: {
|
|
get() {
|
|
return context.result.getLastRecallSentUserMessage();
|
|
},
|
|
set(value) {
|
|
context.result.recordRecallSentUserMessage(
|
|
value?.messageId,
|
|
value?.text || "",
|
|
value?.source,
|
|
);
|
|
},
|
|
configurable: true,
|
|
},
|
|
});
|
|
const originalApplyFinalRecallInjectionForGeneration =
|
|
context.result.applyFinalRecallInjectionForGeneration;
|
|
context.applyFinalRecallInjectionForGeneration = (payload = {}) => {
|
|
context.applyFinalCalls.push({ ...payload });
|
|
if (realApplyFinal) {
|
|
return originalApplyFinalRecallInjectionForGeneration(payload);
|
|
}
|
|
return {
|
|
source: "fresh",
|
|
targetUserMessageIndex: null,
|
|
};
|
|
};
|
|
context.runRecall = async (options = {}) => {
|
|
context.runRecallCalls.push({ ...options });
|
|
const overrideUserMessage = String(
|
|
options.overrideUserMessage || options.userMessage || "",
|
|
);
|
|
return {
|
|
status: "completed",
|
|
didRecall: true,
|
|
ok: true,
|
|
injectionText: `注入:${overrideUserMessage}`,
|
|
deliveryMode: String(options.deliveryMode || "immediate"),
|
|
source: options.overrideSource,
|
|
sourceLabel: options.overrideSourceLabel,
|
|
reason: options.overrideReason,
|
|
sourceCandidates: Array.isArray(options.sourceCandidates)
|
|
? options.sourceCandidates.map((candidate) => ({ ...candidate }))
|
|
: [],
|
|
selectedNodeIds: ["node-test-1"],
|
|
retrievalMeta: {
|
|
vectorHits: 1,
|
|
vectorMergedHits: 0,
|
|
diffusionHits: 0,
|
|
candidatePoolAfterDpp: 1,
|
|
},
|
|
llmMeta: {
|
|
status: "disabled",
|
|
reason: "test-disabled",
|
|
candidatePool: 0,
|
|
},
|
|
stats: {
|
|
coreCount: 1,
|
|
recallCount: 1,
|
|
},
|
|
};
|
|
};
|
|
context.runExtraction = async (...args) => {
|
|
context.runExtractionCalls.push(args);
|
|
return {
|
|
ok: true,
|
|
};
|
|
};
|
|
context.invokeOnMessageSent = (messageId = null) =>
|
|
onMessageSentController(
|
|
{
|
|
getContext: context.getContext,
|
|
isTrivialUserInput,
|
|
recordRecallSentUserMessage: context.result.recordRecallSentUserMessage,
|
|
refreshPersistedRecallMessageUi: () => {
|
|
context.recallUiRefreshCalls += 1;
|
|
},
|
|
},
|
|
messageId,
|
|
);
|
|
context.invokeOnMessageReceived = (messageId = null, type = "") =>
|
|
onMessageReceivedController(
|
|
{
|
|
console,
|
|
consumeCurrentGenerationTrivialSkip:
|
|
context.result.consumeCurrentGenerationTrivialSkip,
|
|
createRecallInputRecord,
|
|
deferAutoExtraction: context.result.deferAutoExtraction,
|
|
getContext: context.getContext,
|
|
getCurrentGraph: () => context.currentGraph,
|
|
getGraphPersistenceState: () => context.result.getGraphPersistenceState(),
|
|
getIsHostGenerationRunning: () =>
|
|
context.result.getIsHostGenerationRunning(),
|
|
getPendingHostGenerationInputSnapshot:
|
|
context.result.getPendingHostGenerationInputSnapshot,
|
|
getPendingRecallSendIntent: () =>
|
|
context.result.getPendingRecallSendIntent(),
|
|
getLastProcessedAssistantFloor: () => -1,
|
|
getSettings: () => context.settings,
|
|
isAssistantChatMessage: (message) =>
|
|
Boolean(message) && !message.is_user && !message.is_system,
|
|
isFreshRecallInputRecord,
|
|
isGraphMetadataWriteAllowed: () => true,
|
|
syncGraphLoadFromLiveContext: () => {},
|
|
maybeCaptureGraphShadowSnapshot: () => {},
|
|
maybeFlushQueuedGraphPersist: () => {},
|
|
notifyExtractionIssue: (message) => {
|
|
context.extractionIssues.push(String(message || ""));
|
|
},
|
|
queueMicrotask: (task) => task(),
|
|
resolveAutoExtractionPlan: (options = {}) =>
|
|
resolveAutoExtractionPlanController(
|
|
{
|
|
getAssistantTurns(chat = []) {
|
|
return chat.flatMap((message, index) =>
|
|
!message?.is_user && !message?.is_system ? [index] : [],
|
|
);
|
|
},
|
|
getLastProcessedAssistantFloor: () => -1,
|
|
getSettings: () => context.settings,
|
|
getSmartTriggerDecision: () => ({
|
|
triggered: false,
|
|
score: 0,
|
|
reasons: [],
|
|
}),
|
|
},
|
|
options,
|
|
),
|
|
runExtraction: context.runExtraction,
|
|
refreshPersistedRecallMessageUi: () => {
|
|
context.recallUiRefreshCalls += 1;
|
|
},
|
|
setPendingHostGenerationInputSnapshot: () => {},
|
|
setPendingRecallSendIntent: (record) => {
|
|
if (record?.text) {
|
|
context.result.recordRecallSendIntent(
|
|
record.text || "",
|
|
record.source,
|
|
);
|
|
return;
|
|
}
|
|
context.result.clearPendingRecallSendIntent();
|
|
},
|
|
},
|
|
messageId,
|
|
type,
|
|
);
|
|
return context;
|
|
});
|
|
}
|