mirror of
https://github.com/Youzini-afk/ST-Bionic-Memory-Ecology.git
synced 2026-06-13 18:31:16 +08:00
Harden post-refactor test and check guardrails
This commit is contained in:
@@ -1,86 +1,9 @@
|
||||
import assert from "node:assert/strict";
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import vm from "node:vm";
|
||||
|
||||
async function loadDefaultSettings() {
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const indexPath = path.resolve(__dirname, "../index.js");
|
||||
const source = await fs.readFile(indexPath, "utf8");
|
||||
const settingsMatch = source.match(/const defaultSettings = \{[\s\S]*?^\};/m);
|
||||
|
||||
if (!settingsMatch) {
|
||||
throw new Error("无法从 index.js 提取 defaultSettings");
|
||||
}
|
||||
|
||||
const context = vm.createContext({
|
||||
createDefaultTaskProfiles() {
|
||||
return {
|
||||
extract: { activeProfileId: "default", profiles: [] },
|
||||
recall: { activeProfileId: "default", profiles: [] },
|
||||
compress: { activeProfileId: "default", profiles: [] },
|
||||
synopsis: { activeProfileId: "default", profiles: [] },
|
||||
reflection: { activeProfileId: "default", profiles: [] },
|
||||
consolidation: { activeProfileId: "default", profiles: [] },
|
||||
};
|
||||
},
|
||||
});
|
||||
const script = new vm.Script(`
|
||||
${settingsMatch[0]}
|
||||
this.defaultSettings = defaultSettings;
|
||||
`);
|
||||
script.runInContext(context);
|
||||
return context.defaultSettings;
|
||||
}
|
||||
|
||||
async function loadSettingsCompatHelpers() {
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const indexPath = path.resolve(__dirname, "../index.js");
|
||||
const source = await fs.readFile(indexPath, "utf8");
|
||||
const settingsMatch = source.match(/const defaultSettings = \{[\s\S]*?^\};/m);
|
||||
const compatMatch = source.match(
|
||||
/function migrateLegacyAutoMaintenanceSettings\(loaded = \{\}\) \{[\s\S]*?^}\r?\n/m,
|
||||
);
|
||||
const mergeMatch = source.match(
|
||||
/function mergePersistedSettings\(loaded = \{\}\) \{[\s\S]*?^}\r?\n/m,
|
||||
);
|
||||
|
||||
if (!settingsMatch || !compatMatch || !mergeMatch) {
|
||||
throw new Error("无法从 index.js 提取设置兼容辅助函数");
|
||||
}
|
||||
|
||||
const context = vm.createContext({
|
||||
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)));
|
||||
},
|
||||
createDefaultTaskProfiles() {
|
||||
return {
|
||||
extract: { activeProfileId: "default", profiles: [] },
|
||||
recall: { activeProfileId: "default", profiles: [] },
|
||||
compress: { activeProfileId: "default", profiles: [] },
|
||||
synopsis: { activeProfileId: "default", profiles: [] },
|
||||
reflection: { activeProfileId: "default", profiles: [] },
|
||||
consolidation: { activeProfileId: "default", profiles: [] },
|
||||
};
|
||||
},
|
||||
});
|
||||
const script = new vm.Script(`
|
||||
${settingsMatch[0]}
|
||||
${compatMatch[0]}
|
||||
${mergeMatch[0]}
|
||||
this.mergePersistedSettings = mergePersistedSettings;
|
||||
`);
|
||||
script.runInContext(context);
|
||||
return {
|
||||
mergePersistedSettings: context.mergePersistedSettings,
|
||||
};
|
||||
}
|
||||
|
||||
const defaultSettings = await loadDefaultSettings();
|
||||
const { mergePersistedSettings } = await loadSettingsCompatHelpers();
|
||||
import {
|
||||
defaultSettings,
|
||||
mergePersistedSettings,
|
||||
} from "../runtime/settings-defaults.js";
|
||||
|
||||
assert.equal(defaultSettings.extractContextTurns, 2);
|
||||
assert.equal(defaultSettings.extractAutoDelayLatestAssistant, false);
|
||||
|
||||
@@ -49,6 +49,11 @@ import {
|
||||
} from "../retrieval/recall-persistence.js";
|
||||
import { getNodeDisplayName } from "../graph/node-labels.js";
|
||||
import { normalizeGraphRuntimeState } from "../runtime/runtime-state.js";
|
||||
import {
|
||||
defaultSettings,
|
||||
getPersistedSettingsSnapshot,
|
||||
mergePersistedSettings,
|
||||
} from "../runtime/settings-defaults.js";
|
||||
import {
|
||||
clampFloat,
|
||||
clampInt,
|
||||
@@ -276,6 +281,9 @@ async function createGraphPersistenceHarness({
|
||||
extension_settings: {
|
||||
[MODULE_NAME]: {},
|
||||
},
|
||||
defaultSettings,
|
||||
getPersistedSettingsSnapshot,
|
||||
mergePersistedSettings,
|
||||
migrateLegacyTaskProfiles(settings = {}) {
|
||||
return {
|
||||
taskProfilesVersion: Number(settings?.taskProfilesVersion || 0),
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
GRAPH_PERSISTENCE_META_KEY,
|
||||
MODULE_NAME,
|
||||
} from "../../graph/graph-persistence.js";
|
||||
import { getSmartTriggerDecision } from "../../maintenance/smart-trigger.js";
|
||||
import {
|
||||
buildPersistedRecallRecord,
|
||||
bumpPersistedRecallGenerationCount,
|
||||
@@ -40,6 +41,10 @@ import {
|
||||
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");
|
||||
@@ -81,7 +86,8 @@ export function createGenerationRecallHarness(options = {}) {
|
||||
result: null,
|
||||
currentGraph: {},
|
||||
_panelModule: null,
|
||||
defaultSettings: {},
|
||||
defaultSettings,
|
||||
mergePersistedSettings,
|
||||
settings: {},
|
||||
graphPersistenceState: createGraphPersistenceState(),
|
||||
extension_settings: { [MODULE_NAME]: {} },
|
||||
@@ -121,6 +127,7 @@ export function createGenerationRecallHarness(options = {}) {
|
||||
[...chat].reverse().find((message) => message?.is_user) || null,
|
||||
getLastNonSystemChatMessage: (chat = []) =>
|
||||
[...chat].reverse().find((message) => !message?.is_system) || null,
|
||||
getSmartTriggerDecision,
|
||||
getSendTextareaValue: () => context.__sendTextareaValue,
|
||||
getRecallUserMessageSourceLabel: (source = "") => source,
|
||||
getRecallUserMessageSourceLabelController: (source = "") => source,
|
||||
|
||||
@@ -1,35 +1,6 @@
|
||||
import assert from "node:assert/strict";
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import vm from "node:vm";
|
||||
|
||||
async function loadSmartTriggerDecision() {
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const indexPath = path.resolve(__dirname, "../index.js");
|
||||
const source = await fs.readFile(indexPath, "utf8");
|
||||
const keywordMatch = source.match(
|
||||
/const DEFAULT_TRIGGER_KEYWORDS = \[[\s\S]*?\];/m,
|
||||
);
|
||||
const fnMatch = source.match(
|
||||
/export function getSmartTriggerDecision\(chat, lastProcessed, settings\) \{[\s\S]*?^\}/m,
|
||||
);
|
||||
|
||||
if (!keywordMatch || !fnMatch) {
|
||||
throw new Error("无法从 index.js 提取 smart trigger 实现");
|
||||
}
|
||||
|
||||
const context = vm.createContext({});
|
||||
const script = new vm.Script(`
|
||||
${keywordMatch[0]}
|
||||
${fnMatch[0].replace("export function", "function")}
|
||||
this.getSmartTriggerDecision = getSmartTriggerDecision;
|
||||
`);
|
||||
script.runInContext(context);
|
||||
return context.getSmartTriggerDecision;
|
||||
}
|
||||
|
||||
const getSmartTriggerDecision = await loadSmartTriggerDecision();
|
||||
import { getSmartTriggerDecision } from "../maintenance/smart-trigger.js";
|
||||
|
||||
const noTrigger = getSmartTriggerDecision(
|
||||
[
|
||||
@@ -61,7 +32,7 @@ const customTrigger = getSmartTriggerDecision(
|
||||
{ triggerPatterns: "真相|背叛", smartTriggerThreshold: 2 },
|
||||
);
|
||||
assert.equal(customTrigger.triggered, true);
|
||||
assert.ok(customTrigger.reasons.some((r) => r.includes("自定义触发")));
|
||||
assert.ok(customTrigger.reasons.some((reason) => reason.includes("自定义触发")));
|
||||
|
||||
const ignoresProcessedMessages = getSmartTriggerDecision(
|
||||
[
|
||||
|
||||
Reference in New Issue
Block a user