Harden post-refactor test and check guardrails

This commit is contained in:
Youzini-afk
2026-04-08 03:02:55 +08:00
parent 6060416c17
commit 9939734bcb
10 changed files with 507 additions and 432 deletions

View File

@@ -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);

View File

@@ -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),

View File

@@ -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,

View File

@@ -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(
[