import assert from "node:assert/strict";
import {
installResolveHooks,
toDataModuleUrl,
} from "./helpers/register-hooks-compat.mjs";
const extensionsShimSource = [
"export const extension_settings = {};",
"export function getContext() {",
" return globalThis.__stBmeTestContext || {",
" chat: [],",
" chatMetadata: {},",
" extensionSettings: {},",
" powerUserSettings: {},",
" characters: {},",
" characterId: null,",
" name1: '玩家',",
" name2: '艾琳',",
" chatId: 'test-chat',",
" };",
"}",
].join("\n");
const scriptShimSource = [
"export function getRequestHeaders() {",
" return {};",
"}",
"export function substituteParamsExtended(value) {",
" return String(value ?? '');",
"}",
].join("\n");
const openAiShimSource = [
"export const chat_completion_sources = {};",
"export async function sendOpenAIRequest() {",
" throw new Error('sendOpenAIRequest should not be called in extractor-input-context test');",
"}",
].join("\n");
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 { DEFAULT_NODE_SCHEMA } = await import("../graph/schema.js");
const { extractMemories } = await import("../maintenance/extractor.js");
function setTestOverrides(overrides = {}) {
globalThis.__stBmeTestOverrides = overrides;
return () => {
delete globalThis.__stBmeTestOverrides;
};
}
globalThis.__stBmeTestContext = {
chat: [],
chatMetadata: {},
extensionSettings: {},
powerUserSettings: {},
characters: {},
characterId: null,
name1: "玩家",
name2: "艾琳",
chatId: "test-chat",
};
const graph = createEmptyGraph();
let captured = null;
const restore = setTestOverrides({
llm: {
async callLLMForJSON(payload) {
captured = payload;
return {
operations: [],
cognitionUpdates: [],
regionUpdates: {},
};
},
},
});
try {
const result = await extractMemories({
graph,
messages: [
{
seq: 10,
role: "assistant",
content: "隐式思维继续说明",
name: "艾琳",
speaker: "艾琳",
},
{
seq: 11,
role: "user",
content: "用户输入",
name: "玩家",
speaker: "玩家",
},
],
startSeq: 10,
endSeq: 11,
schema: DEFAULT_NODE_SCHEMA,
embeddingConfig: null,
settings: {
extractAssistantExcludeTags: "think",
},
});
assert.equal(result.success, true);
assert.ok(captured);
assert.ok(captured.debugContext);
assert.ok(captured.debugContext.inputContext);
assert.equal(captured.debugContext.inputContext.rawMessageCount, 2);
assert.equal(captured.debugContext.inputContext.filteredMessageCount, 2);
assert.equal(captured.debugContext.inputContext.changedAssistantMessageCount, 1);
assert.equal(captured.debugContext.inputContext.excludedAssistantMessageCount, 1);
const recentBlock = (Array.isArray(captured.promptMessages) ? captured.promptMessages : []).find(
(message) => message.sourceKey === "recentMessages",
);
assert.ok(recentBlock);
assert.match(String(recentBlock?.content || ""), /#10 \[assistant\|艾琳\]: 继续说明/);
assert.match(String(recentBlock?.content || ""), /#11 \[user\|玩家\]: 用户输入/);
assert.doesNotMatch(String(recentBlock?.content || ""), /隐式思维|/);
} finally {
restore();
}
console.log("extractor-input-context tests passed");