mirror of
https://github.com/Youzini-afk/ST-Bionic-Memory-Ecology.git
synced 2026-06-13 18:31:16 +08:00
Merge branch 'main' into main
This commit is contained in:
@@ -5,6 +5,7 @@ import {
|
||||
resetHideState,
|
||||
} from "../ui/hide-engine.js";
|
||||
import {
|
||||
buildPluginVisibleChatMessages,
|
||||
buildExtractionMessages,
|
||||
getAssistantTurns,
|
||||
isAssistantChatMessage,
|
||||
@@ -36,6 +37,25 @@ const realSystemMessage = {
|
||||
};
|
||||
assert.equal(isSystemMessageForExtraction(realSystemMessage), true);
|
||||
assert.equal(isAssistantChatMessage(realSystemMessage), false);
|
||||
const pluginVisibleChat = buildPluginVisibleChatMessages([
|
||||
realSystemMessage,
|
||||
managedHiddenAssistant,
|
||||
]);
|
||||
assert.equal(
|
||||
pluginVisibleChat[0].is_system,
|
||||
true,
|
||||
"real system message should remain system in plugin-visible chat",
|
||||
);
|
||||
assert.equal(
|
||||
pluginVisibleChat[1].is_system,
|
||||
false,
|
||||
"BME-managed hidden message should be restored for plugin-internal chat views",
|
||||
);
|
||||
assert.equal(
|
||||
managedHiddenAssistant.is_system,
|
||||
true,
|
||||
"plugin-visible chat clone must not mutate original managed hidden message",
|
||||
);
|
||||
|
||||
function createRuntime(chat, chatId = "chat-a") {
|
||||
return {
|
||||
|
||||
@@ -12,12 +12,15 @@ function createRuntime(persistResult) {
|
||||
nodes: [],
|
||||
edges: [],
|
||||
historyState: {},
|
||||
batchJournal: [],
|
||||
};
|
||||
let processedHistoryUpdates = 0;
|
||||
let persistedGraphSnapshot = null;
|
||||
|
||||
return {
|
||||
graph,
|
||||
processedHistoryUpdates,
|
||||
persistedGraphSnapshot,
|
||||
ensureCurrentGraphRuntimeState() {},
|
||||
throwIfAborted() {},
|
||||
getCurrentGraph() {
|
||||
@@ -64,6 +67,7 @@ function createRuntime(persistResult) {
|
||||
};
|
||||
},
|
||||
async persistExtractionBatchResult() {
|
||||
persistedGraphSnapshot = arguments[0]?.graphSnapshot || null;
|
||||
return persistResult;
|
||||
},
|
||||
finalizeBatchStatus,
|
||||
@@ -73,13 +77,20 @@ function createRuntime(persistResult) {
|
||||
updateProcessedHistorySnapshot() {
|
||||
processedHistoryUpdates += 1;
|
||||
},
|
||||
appendBatchJournal() {},
|
||||
appendBatchJournal(targetGraph, entry) {
|
||||
if (!targetGraph.batchJournal) targetGraph.batchJournal = [];
|
||||
targetGraph.batchJournal.push(entry);
|
||||
},
|
||||
createBatchJournalEntry() {
|
||||
return { id: "journal-1" };
|
||||
return { id: "journal-1", processedRange: [5, 5] };
|
||||
},
|
||||
computePostProcessArtifacts() {
|
||||
return [];
|
||||
},
|
||||
applyProcessedHistorySnapshotToGraph(targetGraph, _chat, floor) {
|
||||
targetGraph.historyState.lastProcessedAssistantFloor = floor;
|
||||
targetGraph.lastProcessedSeq = floor;
|
||||
},
|
||||
getGraphPersistenceState() {
|
||||
return { chatId: "chat-test" };
|
||||
},
|
||||
@@ -87,6 +98,9 @@ function createRuntime(persistResult) {
|
||||
get processedHistoryUpdates() {
|
||||
return processedHistoryUpdates;
|
||||
},
|
||||
get persistedGraphSnapshot() {
|
||||
return persistedGraphSnapshot;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -119,6 +133,14 @@ function createRuntime(persistResult) {
|
||||
runtime.graph.historyState.lastBatchStatus.historyAdvanceAllowed,
|
||||
false,
|
||||
);
|
||||
assert.equal(
|
||||
runtime.persistedGraphSnapshot?.historyState?.lastProcessedAssistantFloor,
|
||||
5,
|
||||
);
|
||||
assert.equal(
|
||||
runtime.persistedGraphSnapshot?.batchJournal?.length,
|
||||
1,
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -150,6 +172,14 @@ function createRuntime(persistResult) {
|
||||
runtime.graph.historyState.lastBatchStatus.historyAdvanceAllowed,
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
runtime.persistedGraphSnapshot?.historyState?.lastProcessedAssistantFloor,
|
||||
5,
|
||||
);
|
||||
assert.equal(
|
||||
runtime.persistedGraphSnapshot?.batchJournal?.length,
|
||||
1,
|
||||
);
|
||||
}
|
||||
|
||||
console.log("extraction-persistence-gating tests passed");
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { registerHooks } from "node:module";
|
||||
import {
|
||||
installResolveHooks,
|
||||
toDataModuleUrl,
|
||||
} from "./helpers/register-hooks-compat.mjs";
|
||||
|
||||
const extensionsShimSource = [
|
||||
"export const extension_settings = {};",
|
||||
@@ -34,39 +37,30 @@ const openAiShimSource = [
|
||||
"}",
|
||||
].join("\n");
|
||||
|
||||
registerHooks({
|
||||
resolve(specifier, context, nextResolve) {
|
||||
if (
|
||||
specifier === "../../../extensions.js" ||
|
||||
specifier === "../../../../extensions.js" ||
|
||||
specifier === "../../../../../extensions.js"
|
||||
) {
|
||||
return {
|
||||
shortCircuit: true,
|
||||
url: `data:text/javascript,${encodeURIComponent(extensionsShimSource)}`,
|
||||
};
|
||||
}
|
||||
if (
|
||||
specifier === "../../../../script.js" ||
|
||||
specifier === "../../../../../script.js"
|
||||
) {
|
||||
return {
|
||||
shortCircuit: true,
|
||||
url: `data:text/javascript,${encodeURIComponent(scriptShimSource)}`,
|
||||
};
|
||||
}
|
||||
if (
|
||||
specifier === "../../../../openai.js" ||
|
||||
specifier === "../../../../../openai.js"
|
||||
) {
|
||||
return {
|
||||
shortCircuit: true,
|
||||
url: `data:text/javascript,${encodeURIComponent(openAiShimSource)}`,
|
||||
};
|
||||
}
|
||||
return nextResolve(specifier, context);
|
||||
installResolveHooks([
|
||||
{
|
||||
specifiers: [
|
||||
"../../../extensions.js",
|
||||
"../../../../extensions.js",
|
||||
"../../../../../extensions.js",
|
||||
],
|
||||
url: toDataModuleUrl(extensionsShimSource),
|
||||
},
|
||||
});
|
||||
{
|
||||
specifiers: [
|
||||
"../../../../script.js",
|
||||
"../../../../../script.js",
|
||||
],
|
||||
url: toDataModuleUrl(scriptShimSource),
|
||||
},
|
||||
{
|
||||
specifiers: [
|
||||
"../../../../openai.js",
|
||||
"../../../../../openai.js",
|
||||
],
|
||||
url: toDataModuleUrl(openAiShimSource),
|
||||
},
|
||||
]);
|
||||
|
||||
const { createEmptyGraph, createNode, addNode } = await import("../graph/graph.js");
|
||||
const { DEFAULT_NODE_SCHEMA } = await import("../graph/schema.js");
|
||||
|
||||
@@ -12,10 +12,13 @@ import {
|
||||
import { onMessageReceivedController } from "../host/event-binding.js";
|
||||
import {
|
||||
buildGraphCommitMarker,
|
||||
buildGraphChatStateSnapshot,
|
||||
canUseGraphChatState,
|
||||
detectIndexedDbSnapshotCommitMarkerMismatch,
|
||||
cloneGraphForPersistence,
|
||||
cloneRuntimeDebugValue,
|
||||
findGraphShadowSnapshotByIntegrity,
|
||||
GRAPH_CHAT_STATE_NAMESPACE,
|
||||
getAcceptedCommitMarkerRevision,
|
||||
getGraphPersistedRevision,
|
||||
getGraphIdentityAliasCandidates,
|
||||
@@ -33,6 +36,7 @@ import {
|
||||
MODULE_NAME,
|
||||
normalizeGraphCommitMarker,
|
||||
readGraphCommitMarker,
|
||||
readGraphChatStateSnapshot,
|
||||
readGraphShadowSnapshot,
|
||||
rememberGraphIdentityAlias,
|
||||
removeGraphShadowSnapshot,
|
||||
@@ -40,6 +44,7 @@ import {
|
||||
shouldPreferShadowSnapshotOverOfficial,
|
||||
stampGraphPersistenceMeta,
|
||||
writeChatMetadataPatch,
|
||||
writeGraphChatStateSnapshot,
|
||||
writeGraphShadowSnapshot,
|
||||
} from "../graph/graph-persistence.js";
|
||||
import {
|
||||
@@ -391,9 +396,12 @@ async function createGraphPersistenceHarness({
|
||||
readPersistedRecallFromUserMessage,
|
||||
cloneGraphForPersistence,
|
||||
buildGraphCommitMarker,
|
||||
buildGraphChatStateSnapshot,
|
||||
canUseGraphChatState,
|
||||
cloneRuntimeDebugValue,
|
||||
detectIndexedDbSnapshotCommitMarkerMismatch,
|
||||
onMessageReceivedController,
|
||||
GRAPH_CHAT_STATE_NAMESPACE,
|
||||
getAcceptedCommitMarkerRevision,
|
||||
getGraphPersistenceMeta,
|
||||
getGraphPersistedRevision,
|
||||
@@ -412,6 +420,7 @@ async function createGraphPersistenceHarness({
|
||||
findGraphShadowSnapshotByIntegrity,
|
||||
normalizeGraphCommitMarker,
|
||||
readGraphCommitMarker,
|
||||
readGraphChatStateSnapshot,
|
||||
readGraphShadowSnapshot,
|
||||
rememberGraphIdentityAlias,
|
||||
removeGraphShadowSnapshot,
|
||||
@@ -419,6 +428,7 @@ async function createGraphPersistenceHarness({
|
||||
shouldPreferShadowSnapshotOverOfficial,
|
||||
stampGraphPersistenceMeta,
|
||||
writeChatMetadataPatch,
|
||||
writeGraphChatStateSnapshot,
|
||||
writeGraphShadowSnapshot,
|
||||
// Shadow snapshot functions need VM-local sessionStorage overrides
|
||||
// because imported versions use the outer globalThis (no sessionStorage)
|
||||
@@ -711,6 +721,7 @@ async function createGraphPersistenceHarness({
|
||||
characterId,
|
||||
groupId,
|
||||
chat,
|
||||
__chatStateStore: new Map(),
|
||||
updateChatMetadata(patch) {
|
||||
const base =
|
||||
this.chatMetadata &&
|
||||
@@ -729,6 +740,36 @@ async function createGraphPersistenceHarness({
|
||||
async saveMetadata() {
|
||||
runtimeContext.__contextImmediateSaveCalls += 1;
|
||||
},
|
||||
async getChatState(namespace) {
|
||||
const key = String(namespace || "").trim().toLowerCase();
|
||||
const value = this.__chatStateStore.get(key);
|
||||
return value == null ? null : structuredClone(value);
|
||||
},
|
||||
async updateChatState(namespace, updater) {
|
||||
const key = String(namespace || "").trim().toLowerCase();
|
||||
if (!key || typeof updater !== "function") {
|
||||
return { ok: false, state: null, updated: false };
|
||||
}
|
||||
const current = this.__chatStateStore.has(key)
|
||||
? structuredClone(this.__chatStateStore.get(key))
|
||||
: {};
|
||||
const next = await updater(structuredClone(current), {
|
||||
attempt: 0,
|
||||
target: null,
|
||||
namespace: key,
|
||||
});
|
||||
if (next == null) {
|
||||
return { ok: true, state: current, updated: false };
|
||||
}
|
||||
const currentJson = JSON.stringify(current);
|
||||
const nextJson = JSON.stringify(next);
|
||||
this.__chatStateStore.set(key, structuredClone(next));
|
||||
return {
|
||||
ok: true,
|
||||
state: structuredClone(next),
|
||||
updated: currentJson !== nextJson,
|
||||
};
|
||||
},
|
||||
},
|
||||
__contextSaveCalls: 0,
|
||||
__contextImmediateSaveCalls: 0,
|
||||
@@ -2332,6 +2373,16 @@ result = {
|
||||
historyAdvanceAllowed: false,
|
||||
historyAdvanced: false,
|
||||
};
|
||||
const committedGraph = structuredClone(graph);
|
||||
committedGraph.historyState.lastProcessedAssistantFloor = 1;
|
||||
committedGraph.lastProcessedSeq = 1;
|
||||
committedGraph.batchJournal = [
|
||||
{
|
||||
id: "journal-queued-1",
|
||||
processedRange: [1, 1],
|
||||
createdAt: Date.now(),
|
||||
},
|
||||
];
|
||||
harness.api.setCurrentGraph(graph);
|
||||
harness.api.setGraphPersistenceState({
|
||||
loadState: "loaded",
|
||||
@@ -2344,6 +2395,14 @@ result = {
|
||||
pendingPersist: true,
|
||||
writesBlocked: false,
|
||||
});
|
||||
harness.api.writeGraphShadowSnapshot(
|
||||
"chat-pending-persist-retry",
|
||||
committedGraph,
|
||||
{
|
||||
revision: 7,
|
||||
reason: "queued-persist-authoritative",
|
||||
},
|
||||
);
|
||||
harness.runtimeContext.__markSyncDirtyShouldThrow = true;
|
||||
|
||||
const result = await harness.api.retryPendingGraphPersist({
|
||||
@@ -2369,6 +2428,15 @@ result = {
|
||||
harness.api.getCurrentGraph().historyState.lastBatchStatus.persistence.outcome,
|
||||
"saved",
|
||||
);
|
||||
assert.equal(
|
||||
harness.api.getCurrentGraph().batchJournal?.length,
|
||||
1,
|
||||
"pending persist retry 应把 authoritative batch journal 回填到 runtime graph",
|
||||
);
|
||||
assert.equal(
|
||||
harness.api.getCurrentGraph().batchJournal?.[0]?.id,
|
||||
"journal-queued-1",
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -2648,4 +2716,111 @@ result = {
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
const harness = await createGraphPersistenceHarness({
|
||||
chatId: "chat-state-save",
|
||||
globalChatId: "chat-state-save",
|
||||
chatMetadata: {
|
||||
integrity: "meta-chat-state-save",
|
||||
},
|
||||
indexedDbSnapshot: {
|
||||
meta: {
|
||||
chatId: "chat-state-save",
|
||||
revision: 0,
|
||||
},
|
||||
nodes: [],
|
||||
edges: [],
|
||||
tombstones: [],
|
||||
state: {
|
||||
lastProcessedFloor: -1,
|
||||
extractionCount: 0,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const graph = stampPersistedGraph(
|
||||
createMeaningfulGraph("chat-state-save", "sidecar"),
|
||||
{
|
||||
revision: 7,
|
||||
integrity: "meta-chat-state-save",
|
||||
chatId: "chat-state-save",
|
||||
reason: "chat-state-seed",
|
||||
},
|
||||
);
|
||||
|
||||
const result = await harness.runtimeContext.persistGraphToHostChatState(
|
||||
harness.runtimeContext.__chatContext,
|
||||
{
|
||||
graph,
|
||||
revision: 7,
|
||||
reason: "chat-state-direct-save",
|
||||
storageTier: "chat-state",
|
||||
accepted: true,
|
||||
lastProcessedAssistantFloor: 6,
|
||||
extractionCount: 3,
|
||||
mode: "primary",
|
||||
},
|
||||
);
|
||||
|
||||
assert.equal(result.saved, true);
|
||||
const stored = await harness.runtimeContext.__chatContext.getChatState(
|
||||
GRAPH_CHAT_STATE_NAMESPACE,
|
||||
);
|
||||
assert.equal(stored?.revision, 7);
|
||||
assert.equal(stored?.commitMarker?.storageTier, "chat-state");
|
||||
assert.equal(
|
||||
harness.api.getGraphPersistenceState().dualWriteLastResult?.target,
|
||||
"chat-state",
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
const harness = await createGraphPersistenceHarness({
|
||||
chatId: "chat-state-read",
|
||||
globalChatId: "chat-state-read",
|
||||
chatMetadata: {
|
||||
integrity: "meta-chat-state-read",
|
||||
},
|
||||
});
|
||||
|
||||
const sidecarGraph = stampPersistedGraph(
|
||||
createMeaningfulGraph("chat-state-read", "sidecar-read"),
|
||||
{
|
||||
revision: 9,
|
||||
integrity: "meta-chat-state-read",
|
||||
chatId: "chat-state-read",
|
||||
reason: "chat-state-read-seed",
|
||||
},
|
||||
);
|
||||
harness.runtimeContext.__chatContext.__chatStateStore.set(
|
||||
GRAPH_CHAT_STATE_NAMESPACE,
|
||||
buildGraphChatStateSnapshot(sidecarGraph, {
|
||||
revision: 9,
|
||||
storageTier: "chat-state",
|
||||
accepted: true,
|
||||
reason: "chat-state-read-seed",
|
||||
chatId: "chat-state-read",
|
||||
integrity: "meta-chat-state-read",
|
||||
lastProcessedAssistantFloor: 6,
|
||||
extractionCount: 3,
|
||||
}),
|
||||
);
|
||||
|
||||
const result = await harness.runtimeContext.readGraphChatStateSnapshot(
|
||||
harness.runtimeContext.__chatContext,
|
||||
{
|
||||
namespace: GRAPH_CHAT_STATE_NAMESPACE,
|
||||
},
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
harness.runtimeContext.canUseGraphChatState(
|
||||
harness.runtimeContext.__chatContext,
|
||||
),
|
||||
true,
|
||||
);
|
||||
assert.equal(result?.revision, 9);
|
||||
assert.equal(result?.commitMarker?.storageTier, "chat-state");
|
||||
}
|
||||
|
||||
console.log("graph-persistence tests passed");
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
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,
|
||||
@@ -125,12 +126,22 @@ export function createGenerationRecallHarness(options = {}) {
|
||||
isTrivialUserInput,
|
||||
getAssistantTurns: (chat = []) =>
|
||||
chat.flatMap((message, index) =>
|
||||
!message?.is_user && !message?.is_system ? [index] : [],
|
||||
!message?.is_user &&
|
||||
!isSystemMessageForExtraction(message, { index, chat })
|
||||
? [index]
|
||||
: [],
|
||||
),
|
||||
isSystemMessageForExtraction,
|
||||
getLatestUserChatMessage: (chat = []) =>
|
||||
[...chat].reverse().find((message) => message?.is_user) || null,
|
||||
getLastNonSystemChatMessage: (chat = []) =>
|
||||
[...chat].reverse().find((message) => !message?.is_system) || null,
|
||||
[...chat]
|
||||
.map((message, index) => ({ message, index }))
|
||||
.reverse()
|
||||
.find(
|
||||
({ message, index }) =>
|
||||
!isSystemMessageForExtraction(message, { index, chat }),
|
||||
)?.message || null,
|
||||
getSmartTriggerDecision,
|
||||
getSendTextareaValue: () => context.__sendTextareaValue,
|
||||
getRecallUserMessageSourceLabel: (source = "") => source,
|
||||
|
||||
54
tests/helpers/register-hooks-compat.mjs
Normal file
54
tests/helpers/register-hooks-compat.mjs
Normal file
@@ -0,0 +1,54 @@
|
||||
import { register, registerHooks } from "node:module";
|
||||
|
||||
export function toDataModuleUrl(source = "") {
|
||||
return `data:text/javascript,${encodeURIComponent(String(source || ""))}`;
|
||||
}
|
||||
|
||||
export function installResolveHooks(entries = []) {
|
||||
const normalizedEntries = (Array.isArray(entries) ? entries : [])
|
||||
.map((entry) => ({
|
||||
specifiers: Array.isArray(entry?.specifiers)
|
||||
? entry.specifiers.map((value) => String(value || "")).filter(Boolean)
|
||||
: [],
|
||||
url: String(entry?.url || ""),
|
||||
}))
|
||||
.filter((entry) => entry.specifiers.length > 0 && entry.url);
|
||||
|
||||
if (typeof registerHooks === "function") {
|
||||
registerHooks({
|
||||
resolve(specifier, context, nextResolve) {
|
||||
for (const entry of normalizedEntries) {
|
||||
if (entry.specifiers.includes(specifier)) {
|
||||
return {
|
||||
shortCircuit: true,
|
||||
url: entry.url,
|
||||
};
|
||||
}
|
||||
}
|
||||
return nextResolve(specifier, context);
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof register === "function") {
|
||||
const loaderSource = `
|
||||
const entries = ${JSON.stringify(normalizedEntries)};
|
||||
export async function resolve(specifier, context, nextResolve) {
|
||||
for (const entry of entries) {
|
||||
if (Array.isArray(entry.specifiers) && entry.specifiers.includes(specifier)) {
|
||||
return {
|
||||
shortCircuit: true,
|
||||
url: entry.url,
|
||||
};
|
||||
}
|
||||
}
|
||||
return nextResolve(specifier, context);
|
||||
}
|
||||
`;
|
||||
register(toDataModuleUrl(loaderSource), import.meta.url);
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Error("No compatible module hook API available");
|
||||
}
|
||||
@@ -1,5 +1,9 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { createRequire, registerHooks } from "node:module";
|
||||
import { createRequire } from "node:module";
|
||||
import {
|
||||
installResolveHooks,
|
||||
toDataModuleUrl,
|
||||
} from "./helpers/register-hooks-compat.mjs";
|
||||
|
||||
const extensionsShimSource = [
|
||||
"export const extension_settings = globalThis.__llmModelFetchExtensionSettings || {};",
|
||||
@@ -22,39 +26,30 @@ const openAiShimSource = [
|
||||
"}",
|
||||
].join("\n");
|
||||
|
||||
registerHooks({
|
||||
resolve(specifier, context, nextResolve) {
|
||||
if (
|
||||
specifier === "../../../extensions.js" ||
|
||||
specifier === "../../../../extensions.js" ||
|
||||
specifier === "../../../../../extensions.js"
|
||||
) {
|
||||
return {
|
||||
shortCircuit: true,
|
||||
url: `data:text/javascript,${encodeURIComponent(extensionsShimSource)}`,
|
||||
};
|
||||
}
|
||||
if (
|
||||
specifier === "../../../../script.js" ||
|
||||
specifier === "../../../../../script.js"
|
||||
) {
|
||||
return {
|
||||
shortCircuit: true,
|
||||
url: `data:text/javascript,${encodeURIComponent(scriptShimSource)}`,
|
||||
};
|
||||
}
|
||||
if (
|
||||
specifier === "../../../openai.js" ||
|
||||
specifier === "../../../../openai.js"
|
||||
) {
|
||||
return {
|
||||
shortCircuit: true,
|
||||
url: `data:text/javascript,${encodeURIComponent(openAiShimSource)}`,
|
||||
};
|
||||
}
|
||||
return nextResolve(specifier, context);
|
||||
installResolveHooks([
|
||||
{
|
||||
specifiers: [
|
||||
"../../../extensions.js",
|
||||
"../../../../extensions.js",
|
||||
"../../../../../extensions.js",
|
||||
],
|
||||
url: toDataModuleUrl(extensionsShimSource),
|
||||
},
|
||||
});
|
||||
{
|
||||
specifiers: [
|
||||
"../../../../script.js",
|
||||
"../../../../../script.js",
|
||||
],
|
||||
url: toDataModuleUrl(scriptShimSource),
|
||||
},
|
||||
{
|
||||
specifiers: [
|
||||
"../../../openai.js",
|
||||
"../../../../openai.js",
|
||||
],
|
||||
url: toDataModuleUrl(openAiShimSource),
|
||||
},
|
||||
]);
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const originalRequire = globalThis.require;
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { createRequire, registerHooks } from "node:module";
|
||||
import { createRequire } from "node:module";
|
||||
import {
|
||||
installResolveHooks,
|
||||
toDataModuleUrl,
|
||||
} from "./helpers/register-hooks-compat.mjs";
|
||||
|
||||
const extensionsShimSource = [
|
||||
"export const extension_settings = globalThis.__llmStreamingExtensionSettings || {};",
|
||||
@@ -22,39 +26,30 @@ const openAiShimSource = [
|
||||
"}",
|
||||
].join("\n");
|
||||
|
||||
registerHooks({
|
||||
resolve(specifier, context, nextResolve) {
|
||||
if (
|
||||
specifier === "../../../extensions.js" ||
|
||||
specifier === "../../../../extensions.js" ||
|
||||
specifier === "../../../../../extensions.js"
|
||||
) {
|
||||
return {
|
||||
shortCircuit: true,
|
||||
url: `data:text/javascript,${encodeURIComponent(extensionsShimSource)}`,
|
||||
};
|
||||
}
|
||||
if (
|
||||
specifier === "../../../../script.js" ||
|
||||
specifier === "../../../../../script.js"
|
||||
) {
|
||||
return {
|
||||
shortCircuit: true,
|
||||
url: `data:text/javascript,${encodeURIComponent(scriptShimSource)}`,
|
||||
};
|
||||
}
|
||||
if (
|
||||
specifier === "../../../openai.js" ||
|
||||
specifier === "../../../../openai.js"
|
||||
) {
|
||||
return {
|
||||
shortCircuit: true,
|
||||
url: `data:text/javascript,${encodeURIComponent(openAiShimSource)}`,
|
||||
};
|
||||
}
|
||||
return nextResolve(specifier, context);
|
||||
installResolveHooks([
|
||||
{
|
||||
specifiers: [
|
||||
"../../../extensions.js",
|
||||
"../../../../extensions.js",
|
||||
"../../../../../extensions.js",
|
||||
],
|
||||
url: toDataModuleUrl(extensionsShimSource),
|
||||
},
|
||||
});
|
||||
{
|
||||
specifiers: [
|
||||
"../../../../script.js",
|
||||
"../../../../../script.js",
|
||||
],
|
||||
url: toDataModuleUrl(scriptShimSource),
|
||||
},
|
||||
{
|
||||
specifiers: [
|
||||
"../../../openai.js",
|
||||
"../../../../openai.js",
|
||||
],
|
||||
url: toDataModuleUrl(openAiShimSource),
|
||||
},
|
||||
]);
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const originalRequire = globalThis.require;
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import assert from "node:assert/strict";
|
||||
import fs from "node:fs/promises";
|
||||
import { createRequire, registerHooks } from "node:module";
|
||||
import { createRequire } from "node:module";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import vm from "node:vm";
|
||||
import {
|
||||
installResolveHooks,
|
||||
toDataModuleUrl,
|
||||
} from "./helpers/register-hooks-compat.mjs";
|
||||
import { pruneProcessedMessageHashesFromFloor } from "../maintenance/chat-history.js";
|
||||
import {
|
||||
onBeforeCombinePromptsController,
|
||||
@@ -106,39 +110,30 @@ const openAiShimUrl = `data:text/javascript,${encodeURIComponent(
|
||||
const moduleDir = path.dirname(fileURLToPath(import.meta.url));
|
||||
const indexPath = path.resolve(moduleDir, "../index.js");
|
||||
|
||||
registerHooks({
|
||||
resolve(specifier, context, nextResolve) {
|
||||
if (
|
||||
specifier === "../../../extensions.js" ||
|
||||
specifier === "../../../../extensions.js" ||
|
||||
specifier === "../../../../../extensions.js"
|
||||
) {
|
||||
return {
|
||||
shortCircuit: true,
|
||||
url: extensionsShimUrl,
|
||||
};
|
||||
}
|
||||
if (
|
||||
specifier === "../../../../script.js" ||
|
||||
specifier === "../../../../../script.js"
|
||||
) {
|
||||
return {
|
||||
shortCircuit: true,
|
||||
url: scriptShimUrl,
|
||||
};
|
||||
}
|
||||
if (
|
||||
specifier === "../../../openai.js" ||
|
||||
specifier === "../../../../openai.js"
|
||||
) {
|
||||
return {
|
||||
shortCircuit: true,
|
||||
url: openAiShimUrl,
|
||||
};
|
||||
}
|
||||
return nextResolve(specifier, context);
|
||||
installResolveHooks([
|
||||
{
|
||||
specifiers: [
|
||||
"../../../extensions.js",
|
||||
"../../../../extensions.js",
|
||||
"../../../../../extensions.js",
|
||||
],
|
||||
url: extensionsShimUrl || toDataModuleUrl(extensionsShimSource),
|
||||
},
|
||||
});
|
||||
{
|
||||
specifiers: [
|
||||
"../../../../script.js",
|
||||
"../../../../../script.js",
|
||||
],
|
||||
url: scriptShimUrl || toDataModuleUrl(scriptShimSource),
|
||||
},
|
||||
{
|
||||
specifiers: [
|
||||
"../../../openai.js",
|
||||
"../../../../openai.js",
|
||||
],
|
||||
url: openAiShimUrl || toDataModuleUrl(openAiShimSource),
|
||||
},
|
||||
]);
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const originalRequire = globalThis.require;
|
||||
@@ -3173,6 +3168,7 @@ async function testProcessedHistoryAdvanceTracksCoreExtractionSuccess() {
|
||||
);
|
||||
setBatchStageOutcome(structuralPartial, "finalize", "success");
|
||||
finalizeBatchStatus(structuralPartial);
|
||||
delete structuralPartial.historyAdvanceAllowed;
|
||||
assert.equal(structuralPartial.completed, true);
|
||||
assert.equal(structuralPartial.outcome, "partial");
|
||||
assert.equal(structuralPartial.consistency, "weak");
|
||||
@@ -3186,6 +3182,7 @@ async function testProcessedHistoryAdvanceTracksCoreExtractionSuccess() {
|
||||
setBatchStageOutcome(semanticFailed, "semantic", "failed", "semantic down");
|
||||
setBatchStageOutcome(semanticFailed, "finalize", "success");
|
||||
finalizeBatchStatus(semanticFailed);
|
||||
delete semanticFailed.historyAdvanceAllowed;
|
||||
assert.equal(semanticFailed.completed, true);
|
||||
assert.equal(semanticFailed.outcome, "failed");
|
||||
assert.equal(semanticFailed.consistency, "strong");
|
||||
@@ -3203,9 +3200,10 @@ async function testProcessedHistoryAdvanceTracksCoreExtractionSuccess() {
|
||||
"vector finalize down",
|
||||
);
|
||||
finalizeBatchStatus(finalizeFailed);
|
||||
delete finalizeFailed.historyAdvanceAllowed;
|
||||
assert.equal(finalizeFailed.completed, false);
|
||||
assert.equal(finalizeFailed.outcome, "failed");
|
||||
assert.equal(shouldAdvanceProcessedHistory(finalizeFailed), true);
|
||||
assert.equal(shouldAdvanceProcessedHistory(finalizeFailed), false);
|
||||
|
||||
const fullSuccess = createBatchStatusSkeleton({
|
||||
processedRange: [8, 9],
|
||||
@@ -3216,6 +3214,7 @@ async function testProcessedHistoryAdvanceTracksCoreExtractionSuccess() {
|
||||
setBatchStageOutcome(fullSuccess, "semantic", "success");
|
||||
setBatchStageOutcome(fullSuccess, "finalize", "success");
|
||||
finalizeBatchStatus(fullSuccess);
|
||||
delete fullSuccess.historyAdvanceAllowed;
|
||||
assert.equal(fullSuccess.completed, true);
|
||||
assert.equal(fullSuccess.outcome, "success");
|
||||
assert.equal(fullSuccess.consistency, "strong");
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { registerHooks } from "node:module";
|
||||
import {
|
||||
installResolveHooks,
|
||||
toDataModuleUrl,
|
||||
} from "./helpers/register-hooks-compat.mjs";
|
||||
|
||||
const extensionsShimSource = [
|
||||
"export const extension_settings = {};",
|
||||
@@ -24,30 +27,23 @@ const scriptShimSource = [
|
||||
"}",
|
||||
].join("\n");
|
||||
|
||||
registerHooks({
|
||||
resolve(specifier, context, nextResolve) {
|
||||
if (
|
||||
specifier === "../../../extensions.js" ||
|
||||
specifier === "../../../../extensions.js" ||
|
||||
specifier === "../../../../../extensions.js"
|
||||
) {
|
||||
return {
|
||||
shortCircuit: true,
|
||||
url: `data:text/javascript,${encodeURIComponent(extensionsShimSource)}`,
|
||||
};
|
||||
}
|
||||
if (
|
||||
specifier === "../../../../script.js" ||
|
||||
specifier === "../../../../../script.js"
|
||||
) {
|
||||
return {
|
||||
shortCircuit: true,
|
||||
url: `data:text/javascript,${encodeURIComponent(scriptShimSource)}`,
|
||||
};
|
||||
}
|
||||
return nextResolve(specifier, context);
|
||||
installResolveHooks([
|
||||
{
|
||||
specifiers: [
|
||||
"../../../extensions.js",
|
||||
"../../../../extensions.js",
|
||||
"../../../../../extensions.js",
|
||||
],
|
||||
url: toDataModuleUrl(extensionsShimSource),
|
||||
},
|
||||
});
|
||||
{
|
||||
specifiers: [
|
||||
"../../../../script.js",
|
||||
"../../../../../script.js",
|
||||
],
|
||||
url: toDataModuleUrl(scriptShimSource),
|
||||
},
|
||||
]);
|
||||
|
||||
const { buildTaskLlmPayload, buildTaskPrompt } = await import("../prompting/prompt-builder.js");
|
||||
const { createDefaultTaskProfiles } = await import("../prompting/prompt-profiles.js");
|
||||
@@ -145,7 +141,9 @@ const recallRulesBlock = recallPayload.promptMessages.find(
|
||||
);
|
||||
assert.match(String(recallFormatBlock?.content || ""), /active_owner_keys/);
|
||||
assert.match(String(recallFormatBlock?.content || ""), /active_owner_scores/);
|
||||
assert.match(String(recallFormatBlock?.content || ""), /selected_keys/);
|
||||
assert.match(String(recallRulesBlock?.content || ""), /剧情时间/);
|
||||
assert.match(String(recallRulesBlock?.content || ""), /评分召回/);
|
||||
|
||||
const formatterCalls = [];
|
||||
initializeHostAdapter({
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { createRequire, registerHooks } from "node:module";
|
||||
import { createRequire } from "node:module";
|
||||
import {
|
||||
installResolveHooks,
|
||||
toDataModuleUrl,
|
||||
} from "./helpers/register-hooks-compat.mjs";
|
||||
|
||||
const extensionsShimSource = [
|
||||
"export const extension_settings = globalThis.__promptBuilderMvuExtensionSettings || {};",
|
||||
@@ -35,39 +39,30 @@ const openAiShimSource = [
|
||||
"}",
|
||||
].join("\n");
|
||||
|
||||
registerHooks({
|
||||
resolve(specifier, context, nextResolve) {
|
||||
if (
|
||||
specifier === "../../../extensions.js" ||
|
||||
specifier === "../../../../extensions.js" ||
|
||||
specifier === "../../../../../extensions.js"
|
||||
) {
|
||||
return {
|
||||
shortCircuit: true,
|
||||
url: `data:text/javascript,${encodeURIComponent(extensionsShimSource)}`,
|
||||
};
|
||||
}
|
||||
if (
|
||||
specifier === "../../../../script.js" ||
|
||||
specifier === "../../../../../script.js"
|
||||
) {
|
||||
return {
|
||||
shortCircuit: true,
|
||||
url: `data:text/javascript,${encodeURIComponent(scriptShimSource)}`,
|
||||
};
|
||||
}
|
||||
if (
|
||||
specifier === "../../../openai.js" ||
|
||||
specifier === "../../../../openai.js"
|
||||
) {
|
||||
return {
|
||||
shortCircuit: true,
|
||||
url: `data:text/javascript,${encodeURIComponent(openAiShimSource)}`,
|
||||
};
|
||||
}
|
||||
return nextResolve(specifier, context);
|
||||
installResolveHooks([
|
||||
{
|
||||
specifiers: [
|
||||
"../../../extensions.js",
|
||||
"../../../../extensions.js",
|
||||
"../../../../../extensions.js",
|
||||
],
|
||||
url: toDataModuleUrl(extensionsShimSource),
|
||||
},
|
||||
});
|
||||
{
|
||||
specifiers: [
|
||||
"../../../../script.js",
|
||||
"../../../../../script.js",
|
||||
],
|
||||
url: toDataModuleUrl(scriptShimSource),
|
||||
},
|
||||
{
|
||||
specifiers: [
|
||||
"../../../openai.js",
|
||||
"../../../../openai.js",
|
||||
],
|
||||
url: toDataModuleUrl(openAiShimSource),
|
||||
},
|
||||
]);
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const originalRequire = globalThis.require;
|
||||
|
||||
33
tests/recall-hide-bypass.mjs
Normal file
33
tests/recall-hide-bypass.mjs
Normal file
@@ -0,0 +1,33 @@
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import { buildRecallRecentMessagesController } from "../retrieval/recall-controller.js";
|
||||
|
||||
const chat = [
|
||||
{ is_user: false, is_system: true, mes: "greeting/system" },
|
||||
{
|
||||
is_user: false,
|
||||
is_system: true,
|
||||
mes: "managed hidden assistant",
|
||||
extra: { __st_bme_hide_managed: true },
|
||||
},
|
||||
{ is_user: true, is_system: false, mes: "user message" },
|
||||
{ is_user: false, is_system: true, mes: "real system" },
|
||||
{ is_user: false, is_system: false, mes: "visible assistant" },
|
||||
];
|
||||
|
||||
const recentMessages = buildRecallRecentMessagesController(chat, 6, "", {
|
||||
formatRecallContextLine(message) {
|
||||
return `[${message.is_user ? "user" : "assistant"}]: ${message.mes}`;
|
||||
},
|
||||
normalizeRecallInputText(value = "") {
|
||||
return String(value || "").trim();
|
||||
},
|
||||
});
|
||||
|
||||
assert.deepEqual(recentMessages, [
|
||||
"[assistant]: managed hidden assistant",
|
||||
"[user]: user message",
|
||||
"[assistant]: visible assistant",
|
||||
]);
|
||||
|
||||
console.log("recall-hide-bypass tests passed");
|
||||
@@ -85,7 +85,7 @@ const state = {
|
||||
diffusionCalls: [],
|
||||
llmCalls: [],
|
||||
llmCandidateCount: 0,
|
||||
llmResponse: { selected_ids: ["rule-2", "rule-1"] },
|
||||
llmResponse: { selected_keys: ["R1", "R2"] },
|
||||
llmOptions: [],
|
||||
};
|
||||
|
||||
@@ -447,7 +447,7 @@ state.diffusionCalls.length = 0;
|
||||
state.llmCalls.length = 0;
|
||||
state.llmOptions.length = 0;
|
||||
state.llmCandidateCount = 0;
|
||||
state.llmResponse = { selected_ids: ["rule-2", "rule-1"] };
|
||||
state.llmResponse = { selected_keys: ["R1", "R2"] };
|
||||
const llmPoolResult = await retrieve({
|
||||
graph,
|
||||
userMessage: "请根据规则给出结论",
|
||||
@@ -471,6 +471,23 @@ assert.equal(state.diffusionCalls.length, 0);
|
||||
assert.equal(state.llmCandidateCount, 2);
|
||||
assert.deepEqual(Array.from(llmPoolResult.selectedNodeIds), ["rule-2", "rule-1"]);
|
||||
assert.equal(llmPoolResult.meta.retrieval.llm.status, "llm");
|
||||
assert.equal(
|
||||
llmPoolResult.meta.retrieval.llm.selectionProtocol,
|
||||
"candidate-keys-v1",
|
||||
);
|
||||
assert.deepEqual(
|
||||
Array.from(llmPoolResult.meta.retrieval.llm.rawSelectedKeys),
|
||||
["R1", "R2"],
|
||||
);
|
||||
assert.deepEqual(
|
||||
Array.from(llmPoolResult.meta.retrieval.llm.resolvedSelectedNodeIds),
|
||||
["rule-2", "rule-1"],
|
||||
);
|
||||
assert.equal(
|
||||
llmPoolResult.meta.retrieval.llm.candidateKeyMapPreview?.R1?.nodeId,
|
||||
"rule-2",
|
||||
);
|
||||
assert.equal(llmPoolResult.meta.retrieval.llm.legacySelectionUsed, false);
|
||||
assert.equal(llmPoolResult.meta.retrieval.llm.candidatePool, 2);
|
||||
assert.equal(llmPoolResult.meta.retrieval.vectorMergedHits, 3);
|
||||
assert.equal(llmPoolResult.meta.retrieval.diversityApplied, true);
|
||||
@@ -479,6 +496,135 @@ assert.equal(llmPoolResult.meta.retrieval.candidatePoolAfterDpp, 2);
|
||||
assert.equal(state.llmOptions[0].returnFailureDetails, true);
|
||||
assert.equal(state.llmOptions[0].maxRetries, 2);
|
||||
assert.equal(state.llmOptions[0].maxCompletionTokens, 512);
|
||||
assert.match(String(state.llmCalls[0] || ""), /\[R1\]/);
|
||||
assert.doesNotMatch(String(state.llmCalls[0] || ""), /\[rule-1\]|\[rule-2\]/);
|
||||
|
||||
state.vectorCalls.length = 0;
|
||||
state.diffusionCalls.length = 0;
|
||||
state.llmCalls.length = 0;
|
||||
state.llmOptions.length = 0;
|
||||
state.llmResponse = {
|
||||
selected_keys: ["R2"],
|
||||
selected_ids: ["rule-2"],
|
||||
};
|
||||
const selectedKeysPriorityResult = await retrieve({
|
||||
graph,
|
||||
userMessage: "优先吃新协议",
|
||||
recentMessages: ["用户:测试 selected_keys 优先级"],
|
||||
embeddingConfig: {},
|
||||
schema,
|
||||
options: {
|
||||
topK: 4,
|
||||
maxRecallNodes: 2,
|
||||
enableVectorPrefilter: true,
|
||||
enableGraphDiffusion: false,
|
||||
enableLLMRecall: true,
|
||||
llmCandidatePool: 2,
|
||||
},
|
||||
});
|
||||
assert.deepEqual(Array.from(selectedKeysPriorityResult.selectedNodeIds), ["rule-1"]);
|
||||
assert.equal(
|
||||
selectedKeysPriorityResult.meta.retrieval.llm.selectionProtocol,
|
||||
"candidate-keys-v1",
|
||||
);
|
||||
assert.equal(
|
||||
selectedKeysPriorityResult.meta.retrieval.llm.legacySelectionUsed,
|
||||
false,
|
||||
);
|
||||
|
||||
state.vectorCalls.length = 0;
|
||||
state.diffusionCalls.length = 0;
|
||||
state.llmCalls.length = 0;
|
||||
state.llmOptions.length = 0;
|
||||
state.llmResponse = { selected_ids: ["rule-1"] };
|
||||
const legacySelectionResult = await retrieve({
|
||||
graph,
|
||||
userMessage: "兼容旧 selected_ids",
|
||||
recentMessages: ["用户:测试 legacy 路径"],
|
||||
embeddingConfig: {},
|
||||
schema,
|
||||
options: {
|
||||
topK: 4,
|
||||
maxRecallNodes: 2,
|
||||
enableVectorPrefilter: true,
|
||||
enableGraphDiffusion: false,
|
||||
enableLLMRecall: true,
|
||||
llmCandidatePool: 2,
|
||||
},
|
||||
});
|
||||
assert.deepEqual(Array.from(legacySelectionResult.selectedNodeIds), ["rule-1"]);
|
||||
assert.equal(
|
||||
legacySelectionResult.meta.retrieval.llm.selectionProtocol,
|
||||
"legacy-selected-ids",
|
||||
);
|
||||
assert.equal(
|
||||
legacySelectionResult.meta.retrieval.llm.legacySelectionUsed,
|
||||
true,
|
||||
);
|
||||
|
||||
state.vectorCalls.length = 0;
|
||||
state.diffusionCalls.length = 0;
|
||||
state.llmCalls.length = 0;
|
||||
state.llmOptions.length = 0;
|
||||
state.llmResponse = { selected_keys: [] };
|
||||
const emptySelectionFallbackResult = await retrieve({
|
||||
graph,
|
||||
userMessage: "这次故意空选",
|
||||
recentMessages: ["用户:测试空选回退"],
|
||||
embeddingConfig: {},
|
||||
schema,
|
||||
options: {
|
||||
topK: 4,
|
||||
maxRecallNodes: 2,
|
||||
enableVectorPrefilter: true,
|
||||
enableGraphDiffusion: false,
|
||||
enableLLMRecall: true,
|
||||
llmCandidatePool: 2,
|
||||
},
|
||||
});
|
||||
assert.equal(emptySelectionFallbackResult.meta.retrieval.llm.status, "fallback");
|
||||
assert.equal(
|
||||
emptySelectionFallbackResult.meta.retrieval.llm.fallbackType,
|
||||
"empty-selection",
|
||||
);
|
||||
assert.equal(
|
||||
emptySelectionFallbackResult.meta.retrieval.llm.emptySelectionAccepted,
|
||||
false,
|
||||
);
|
||||
assert.deepEqual(
|
||||
Array.from(emptySelectionFallbackResult.selectedNodeIds),
|
||||
["rule-2", "rule-1"],
|
||||
);
|
||||
|
||||
state.vectorCalls.length = 0;
|
||||
state.diffusionCalls.length = 0;
|
||||
state.llmCalls.length = 0;
|
||||
state.llmOptions.length = 0;
|
||||
state.llmResponse = { selected_keys: ["R99"] };
|
||||
const invalidKeyFallbackResult = await retrieve({
|
||||
graph,
|
||||
userMessage: "这次给无效 key",
|
||||
recentMessages: ["用户:测试无效候选回退"],
|
||||
embeddingConfig: {},
|
||||
schema,
|
||||
options: {
|
||||
topK: 4,
|
||||
maxRecallNodes: 2,
|
||||
enableVectorPrefilter: true,
|
||||
enableGraphDiffusion: false,
|
||||
enableLLMRecall: true,
|
||||
llmCandidatePool: 2,
|
||||
},
|
||||
});
|
||||
assert.equal(invalidKeyFallbackResult.meta.retrieval.llm.status, "fallback");
|
||||
assert.equal(
|
||||
invalidKeyFallbackResult.meta.retrieval.llm.fallbackType,
|
||||
"invalid-candidate",
|
||||
);
|
||||
assert.deepEqual(
|
||||
Array.from(invalidKeyFallbackResult.selectedNodeIds),
|
||||
["rule-2", "rule-1"],
|
||||
);
|
||||
|
||||
state.vectorCalls.length = 0;
|
||||
state.diffusionCalls.length = 0;
|
||||
@@ -792,6 +938,14 @@ const multiOwnerResult = await retrieve({
|
||||
llmCandidatePool: 4,
|
||||
},
|
||||
});
|
||||
assert.equal(
|
||||
multiOwnerResult.meta.retrieval.llm.selectionProtocol,
|
||||
"legacy-selected-ids",
|
||||
);
|
||||
assert.equal(
|
||||
multiOwnerResult.meta.retrieval.llm.legacySelectionUsed,
|
||||
true,
|
||||
);
|
||||
assert.deepEqual(
|
||||
Array.from(multiOwnerResult.meta.retrieval.activeRecallOwnerKeys),
|
||||
["character:艾琳", "character:露西亚"],
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { registerHooks } from "node:module";
|
||||
import {
|
||||
installResolveHooks,
|
||||
toDataModuleUrl,
|
||||
} from "./helpers/register-hooks-compat.mjs";
|
||||
|
||||
const extensionsShimSource = [
|
||||
"export function getContext(...args) {",
|
||||
@@ -10,20 +13,15 @@ const extensionsShimUrl = `data:text/javascript,${encodeURIComponent(
|
||||
extensionsShimSource,
|
||||
)}`;
|
||||
|
||||
registerHooks({
|
||||
resolve(specifier, context, nextResolve) {
|
||||
if (
|
||||
specifier === "../../../extensions.js" ||
|
||||
specifier === "../../../../extensions.js"
|
||||
) {
|
||||
return {
|
||||
shortCircuit: true,
|
||||
url: extensionsShimUrl,
|
||||
};
|
||||
}
|
||||
return nextResolve(specifier, context);
|
||||
installResolveHooks([
|
||||
{
|
||||
specifiers: [
|
||||
"../../../extensions.js",
|
||||
"../../../../extensions.js",
|
||||
],
|
||||
url: extensionsShimUrl || toDataModuleUrl(extensionsShimSource),
|
||||
},
|
||||
});
|
||||
]);
|
||||
|
||||
const originalSillyTavern = globalThis.SillyTavern;
|
||||
const originalGetCurrentChatId = globalThis.getCurrentChatId;
|
||||
@@ -69,6 +67,14 @@ try {
|
||||
},
|
||||
chat: [
|
||||
{ is_user: true, mes: "第一句" },
|
||||
{
|
||||
is_user: false,
|
||||
is_system: true,
|
||||
mes: "被 BME 隐藏的助手楼层",
|
||||
extra: {
|
||||
__st_bme_hide_managed: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
is_user: false,
|
||||
mes: "回应",
|
||||
@@ -115,6 +121,14 @@ try {
|
||||
assert.equal(hostSnapshot.snapshot.variables.local.location, "library");
|
||||
assert.equal(hostSnapshot.snapshot.chat.lastUserMessage, "最后一句");
|
||||
assert.equal(hostSnapshot.snapshot.chat.id, "chat-from-global");
|
||||
assert.equal(
|
||||
hostSnapshot.snapshot.chat.messages[1]?.is_system,
|
||||
false,
|
||||
);
|
||||
assert.equal(
|
||||
hostSnapshot.snapshot.chat.messages[1]?.mes,
|
||||
"被 BME 隐藏的助手楼层",
|
||||
);
|
||||
assert.equal(hostSnapshot.prompt.charName, "Alice");
|
||||
assert.equal(hostSnapshot.prompt.userPersona, "桥接 persona");
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { registerHooks } from "node:module";
|
||||
import {
|
||||
installResolveHooks,
|
||||
toDataModuleUrl,
|
||||
} from "./helpers/register-hooks-compat.mjs";
|
||||
|
||||
const extensionsShimSource = [
|
||||
"export const extension_settings = {};",
|
||||
@@ -34,39 +37,30 @@ const openAiShimSource = [
|
||||
"}",
|
||||
].join("\n");
|
||||
|
||||
registerHooks({
|
||||
resolve(specifier, context, nextResolve) {
|
||||
if (
|
||||
specifier === "../../../extensions.js" ||
|
||||
specifier === "../../../../extensions.js" ||
|
||||
specifier === "../../../../../extensions.js"
|
||||
) {
|
||||
return {
|
||||
shortCircuit: true,
|
||||
url: `data:text/javascript,${encodeURIComponent(extensionsShimSource)}`,
|
||||
};
|
||||
}
|
||||
if (
|
||||
specifier === "../../../../script.js" ||
|
||||
specifier === "../../../../../script.js"
|
||||
) {
|
||||
return {
|
||||
shortCircuit: true,
|
||||
url: `data:text/javascript,${encodeURIComponent(scriptShimSource)}`,
|
||||
};
|
||||
}
|
||||
if (
|
||||
specifier === "../../../openai.js" ||
|
||||
specifier === "../../../../openai.js"
|
||||
) {
|
||||
return {
|
||||
shortCircuit: true,
|
||||
url: `data:text/javascript,${encodeURIComponent(openAiShimSource)}`,
|
||||
};
|
||||
}
|
||||
return nextResolve(specifier, context);
|
||||
installResolveHooks([
|
||||
{
|
||||
specifiers: [
|
||||
"../../../extensions.js",
|
||||
"../../../../extensions.js",
|
||||
"../../../../../extensions.js",
|
||||
],
|
||||
url: toDataModuleUrl(extensionsShimSource),
|
||||
},
|
||||
});
|
||||
{
|
||||
specifiers: [
|
||||
"../../../../script.js",
|
||||
"../../../../../script.js",
|
||||
],
|
||||
url: toDataModuleUrl(scriptShimSource),
|
||||
},
|
||||
{
|
||||
specifiers: [
|
||||
"../../../openai.js",
|
||||
"../../../../openai.js",
|
||||
],
|
||||
url: toDataModuleUrl(openAiShimSource),
|
||||
},
|
||||
]);
|
||||
|
||||
const { createEmptyGraph } = await import("../graph/graph.js");
|
||||
const { appendSummaryEntry } = await import("../graph/summary-state.js");
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { registerHooks } from "node:module";
|
||||
import {
|
||||
installResolveHooks,
|
||||
} from "./helpers/register-hooks-compat.mjs";
|
||||
|
||||
const extensionsShimSource = [
|
||||
"export const extension_settings = globalThis.__taskRegexTestExtensionSettings || {};",
|
||||
@@ -11,21 +13,16 @@ const extensionsShimUrl = `data:text/javascript,${encodeURIComponent(
|
||||
extensionsShimSource,
|
||||
)}`;
|
||||
|
||||
registerHooks({
|
||||
resolve(specifier, context, nextResolve) {
|
||||
if (
|
||||
specifier === "../../../extensions.js" ||
|
||||
specifier === "../../../../extensions.js" ||
|
||||
specifier === "../../../../../extensions.js"
|
||||
) {
|
||||
return {
|
||||
shortCircuit: true,
|
||||
url: extensionsShimUrl,
|
||||
};
|
||||
}
|
||||
return nextResolve(specifier, context);
|
||||
installResolveHooks([
|
||||
{
|
||||
specifiers: [
|
||||
"../../../extensions.js",
|
||||
"../../../../extensions.js",
|
||||
"../../../../../extensions.js",
|
||||
],
|
||||
url: extensionsShimUrl,
|
||||
},
|
||||
});
|
||||
]);
|
||||
|
||||
const originalSillyTavern = globalThis.SillyTavern;
|
||||
const originalGetTavernRegexes = globalThis.getTavernRegexes;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { registerHooks } from "node:module";
|
||||
import {
|
||||
installResolveHooks,
|
||||
} from "./helpers/register-hooks-compat.mjs";
|
||||
|
||||
const extensionsShimSource = [
|
||||
"export const extension_settings = {};",
|
||||
@@ -19,30 +21,23 @@ const scriptShimUrl = `data:text/javascript,${encodeURIComponent(
|
||||
scriptShimSource,
|
||||
)}`;
|
||||
|
||||
registerHooks({
|
||||
resolve(specifier, context, nextResolve) {
|
||||
if (
|
||||
specifier === "../../../extensions.js" ||
|
||||
specifier === "../../../../extensions.js" ||
|
||||
specifier === "../../../../../extensions.js"
|
||||
) {
|
||||
return {
|
||||
shortCircuit: true,
|
||||
url: extensionsShimUrl,
|
||||
};
|
||||
}
|
||||
if (
|
||||
specifier === "../../../../script.js" ||
|
||||
specifier === "../../../../../script.js"
|
||||
) {
|
||||
return {
|
||||
shortCircuit: true,
|
||||
url: scriptShimUrl,
|
||||
};
|
||||
}
|
||||
return nextResolve(specifier, context);
|
||||
installResolveHooks([
|
||||
{
|
||||
specifiers: [
|
||||
"../../../extensions.js",
|
||||
"../../../../extensions.js",
|
||||
"../../../../../extensions.js",
|
||||
],
|
||||
url: extensionsShimUrl,
|
||||
},
|
||||
});
|
||||
{
|
||||
specifiers: [
|
||||
"../../../../script.js",
|
||||
"../../../../../script.js",
|
||||
],
|
||||
url: scriptShimUrl,
|
||||
},
|
||||
]);
|
||||
|
||||
const originalSillyTavern = globalThis.SillyTavern;
|
||||
const originalEjsTemplate = globalThis.EjsTemplate;
|
||||
|
||||
Reference in New Issue
Block a user