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 {", " chat: [],", " chatMetadata: {},", " extensionSettings: {},", " powerUserSettings: {},", " characters: {},", " characterId: null,", " name1: '',", " name2: '',", " chatId: 'test-chat',", " };", "}", ].join("\n"); const scriptShimSource = [ "export function substituteParamsExtended(value) {", " return String(value ?? '');", "}", ].join("\n"); 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 { createDefaultGlobalTaskRegex, createDefaultTaskProfiles, } = await import("../prompting/prompt-profiles.js"); const { initializeHostAdapter } = await import("../host/adapter/index.js"); const settings = { taskProfilesVersion: 3, taskProfiles: createDefaultTaskProfiles(), }; const extractPromptBuild = await buildTaskPrompt(settings, "extract", { taskName: "extract", charDescription: "角色描述", userPersona: "用户设定", recentMessages: "A: 你好\nB: 世界", graphStats: "node_count=3", schema: "event(title, summary)", currentRange: "1 ~ 2", }); const extractPayload = buildTaskLlmPayload(extractPromptBuild, "fallback-user"); assert.equal(extractPayload.systemPrompt, ""); assert.equal(extractPayload.userPrompt, ""); assert.equal( extractPayload.promptMessages.filter((message) => message.role === "user").length, 2, ); assert.deepEqual( extractPayload.promptMessages .filter((message) => message.role === "user") .map((message) => message.blockName), ["输出格式", "行为规则"], ); const extractFormatBlock = extractPayload.promptMessages.find( (message) => message.blockName === "输出格式", ); const extractRulesBlock = extractPayload.promptMessages.find( (message) => message.blockName === "行为规则", ); assert.match(String(extractFormatBlock?.content || ""), /cognitionUpdates/); assert.match(String(extractFormatBlock?.content || ""), /regionUpdates/); assert.match(String(extractFormatBlock?.content || ""), /batchStoryTime/); assert.match(String(extractFormatBlock?.content || ""), /storyTime/); assert.match(String(extractRulesBlock?.content || ""), /涉及到的角色都尽量尝试补 cognitionUpdates/); assert.match(String(extractRulesBlock?.content || ""), /batchStoryTime/); assert.deepEqual( extractPayload.promptMessages .map((message) => message.sourceKey) .filter(Boolean), [ "charDescription", "userPersona", "recentMessages", "graphStats", "schema", "currentRange", ], ); const recallPromptBuild = await buildTaskPrompt(settings, "recall", { taskName: "recall", charDescription: "角色描述", userPersona: "用户设定", recentMessages: "上下文", userMessage: "用户最新发言", candidateNodes: "候选 1\n候选 2", sceneOwnerCandidates: "character:alice\ncharacter:bob", graphStats: "candidate_count=2", }); const recallPayload = buildTaskLlmPayload(recallPromptBuild, "fallback-user"); assert.equal(recallPayload.systemPrompt, ""); assert.equal(recallPayload.userPrompt, ""); assert.equal( recallPayload.promptMessages.filter((message) => message.role === "user").length, 2, ); assert.deepEqual( recallPayload.promptMessages .map((message) => message.sourceKey) .filter(Boolean), [ "charDescription", "userPersona", "recentMessages", "userMessage", "candidateNodes", "sceneOwnerCandidates", "graphStats", ], ); const recallFormatBlock = recallPayload.promptMessages.find( (message) => message.blockName === "输出格式", ); const recallRulesBlock = recallPayload.promptMessages.find( (message) => message.blockName === "行为规则", ); 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 globalRegexPromptBuild = await buildTaskPrompt( { taskProfilesVersion: 3, taskProfiles: createDefaultTaskProfiles(), globalTaskRegex: createDefaultGlobalTaskRegex(), }, "recall", { taskName: "recall", recentMessages: "最近消息 隐藏思维 1. 隐藏选项", userMessage: "用户输入 secret hp=3", candidateNodes: "候选节点 隐藏分析", }, ); assert.doesNotMatch( JSON.stringify(globalRegexPromptBuild), /.*?<\/action>/g, ""); } if (source === "user_input") { return String(text || "").replace(/|<\/u>/g, ""); } return String(text || ""); }, }, }); const regexAwarePromptBuild = await buildTaskPrompt(settings, "extract", { taskName: "extract", charDescription: "", userPersona: "", recentMessages: "这里会被 chatMessages 回填", chatMessages: [ { seq: 36, role: "assistant", content: "挥手继续说明", }, { seq: 37, role: "user", content: "用户输入", }, ], graphStats: "node_count=1", schema: "event(title, summary)", currentRange: "36 ~ 37", }); const regexAwarePayload = buildTaskLlmPayload( regexAwarePromptBuild, "fallback-user", ); const regexAwareRecentBlock = regexAwarePayload.promptMessages.find( (message) => message.sourceKey === "recentMessages", ); assert.match(String(regexAwareRecentBlock?.content || ""), /#36 \[assistant\]: 继续说明/); assert.match(String(regexAwareRecentBlock?.content || ""), /#37 \[user\]: 用户输入/); assert.doesNotMatch(String(regexAwareRecentBlock?.content || ""), /action||<\/u>/i); assert.equal( formatterCalls.some( (call) => call.source === "ai_output" && call.destination === "prompt" && call.options?.depth === 1 && call.options?.isPrompt === true, ), true, ); assert.equal( formatterCalls.some( (call) => call.source === "user_input" && call.destination === "prompt" && call.options?.depth === 0 && call.options?.isPrompt === true, ), true, ); initializeHostAdapter({}); console.log("prompt-builder-defaults tests passed");