mirror of
https://github.com/Youzini-afk/ST-Bionic-Memory-Ecology.git
synced 2026-06-10 00:20:44 +08:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a71fc2c4a8 |
@@ -1,73 +0,0 @@
|
|||||||
export function extractLastNPlots(chat, n) {
|
|
||||||
if (!Array.isArray(chat) || chat.length === 0) return [];
|
|
||||||
const want = Math.max(0, Number(n) || 0);
|
|
||||||
if (!want) return [];
|
|
||||||
|
|
||||||
const plots = [];
|
|
||||||
const plotRe = /<plot\b[^>]*>[\s\S]*?<\/plot>/gi;
|
|
||||||
|
|
||||||
for (let i = chat.length - 1; i >= 0; i--) {
|
|
||||||
const text = chat[i]?.mes ?? '';
|
|
||||||
if (!text) continue;
|
|
||||||
const matches = [...text.matchAll(plotRe)];
|
|
||||||
for (let j = matches.length - 1; j >= 0; j--) {
|
|
||||||
plots.push(matches[j][0]);
|
|
||||||
if (plots.length >= want) return plots;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return plots;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function formatPlotsBlock(plotList) {
|
|
||||||
if (!Array.isArray(plotList) || plotList.length === 0) return '';
|
|
||||||
const chrono = [...plotList].reverse();
|
|
||||||
const lines = [];
|
|
||||||
chrono.forEach((p, idx) => {
|
|
||||||
lines.push(`【plot -${chrono.length - idx}】\n${p}`);
|
|
||||||
});
|
|
||||||
return `<previous_plots>\n${lines.join('\n\n')}\n</previous_plots>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function applyPlannerResultAndSend({
|
|
||||||
textarea,
|
|
||||||
button,
|
|
||||||
rawUserInput = '',
|
|
||||||
filtered = '',
|
|
||||||
plannerRecall = null,
|
|
||||||
plannerPlotRecord = null,
|
|
||||||
runtime = null,
|
|
||||||
plannerState = null,
|
|
||||||
} = {}) {
|
|
||||||
if (!textarea || !button) return { applied: false, reason: 'missing-target' };
|
|
||||||
|
|
||||||
const raw = String(rawUserInput ?? '').trim();
|
|
||||||
const merged = `${raw}\n\n${String(filtered ?? '')}`.trim();
|
|
||||||
textarea.value = merged;
|
|
||||||
if (plannerState && typeof plannerState === 'object') {
|
|
||||||
plannerState.lastInjectedText = merged;
|
|
||||||
}
|
|
||||||
|
|
||||||
let handoffPrepared = false;
|
|
||||||
if (runtime?.preparePlannerRecallHandoff && plannerRecall?.result) {
|
|
||||||
const plotRecordPayload = plannerPlotRecord && typeof plannerPlotRecord === 'object'
|
|
||||||
? {
|
|
||||||
...plannerPlotRecord,
|
|
||||||
rawUserInput: raw,
|
|
||||||
plannerAugmentedMessage: merged,
|
|
||||||
}
|
|
||||||
: null;
|
|
||||||
runtime.preparePlannerRecallHandoff({
|
|
||||||
rawUserInput: raw,
|
|
||||||
plannerAugmentedMessage: merged,
|
|
||||||
plannerRecall,
|
|
||||||
plannerPlotRecord: plotRecordPayload,
|
|
||||||
});
|
|
||||||
handoffPrepared = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (plannerState && typeof plannerState === 'object') {
|
|
||||||
plannerState.bypassNextSend = true;
|
|
||||||
}
|
|
||||||
button.click();
|
|
||||||
return { applied: true, merged, handoffPrepared };
|
|
||||||
}
|
|
||||||
@@ -1,10 +1,6 @@
|
|||||||
import { extension_settings } from '../../../../extensions.js';
|
import { extension_settings } from '../../../../extensions.js';
|
||||||
import { getRequestHeaders, saveSettingsDebounced, substituteParamsExtended } from '../../../../../script.js';
|
import { getRequestHeaders, saveSettingsDebounced, substituteParamsExtended } from '../../../../../script.js';
|
||||||
import { EnaPlannerStorage, migrateFromLWBIfNeeded } from './ena-planner-storage.js';
|
import { EnaPlannerStorage, migrateFromLWBIfNeeded } from './ena-planner-storage.js';
|
||||||
import {
|
|
||||||
applyPlannerResultAndSend,
|
|
||||||
} from './ena-planner-runtime-utils.js';
|
|
||||||
import { readPlannerPlotHistory } from './planner-plot-history.js';
|
|
||||||
import { DEFAULT_PROMPT_BLOCKS, BUILTIN_TEMPLATES } from './ena-planner-presets.js';
|
import { DEFAULT_PROMPT_BLOCKS, BUILTIN_TEMPLATES } from './ena-planner-presets.js';
|
||||||
import {
|
import {
|
||||||
createBuiltinPromptBlock,
|
createBuiltinPromptBlock,
|
||||||
@@ -798,6 +794,38 @@ function collectRecentChatSnippet(chat, maxMessages) {
|
|||||||
* Plot extraction
|
* Plot extraction
|
||||||
* --------------------------
|
* --------------------------
|
||||||
*/
|
*/
|
||||||
|
function extractLastNPlots(chat, n) {
|
||||||
|
if (!Array.isArray(chat) || chat.length === 0) return [];
|
||||||
|
const want = Math.max(0, Number(n) || 0);
|
||||||
|
if (!want) return [];
|
||||||
|
|
||||||
|
const plots = [];
|
||||||
|
const plotRe = /<plot\b[^>]*>[\s\S]*?<\/plot>/gi;
|
||||||
|
|
||||||
|
for (let i = chat.length - 1; i >= 0; i--) {
|
||||||
|
const text = chat[i]?.mes ?? '';
|
||||||
|
if (!text) continue;
|
||||||
|
const matches = [...text.matchAll(plotRe)];
|
||||||
|
for (let j = matches.length - 1; j >= 0; j--) {
|
||||||
|
plots.push(matches[j][0]);
|
||||||
|
if (plots.length >= want) return plots;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return plots;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatPlotsBlock(plotList) {
|
||||||
|
if (!Array.isArray(plotList) || plotList.length === 0) return '';
|
||||||
|
// plotList is [newest, ..., oldest] from extractLastNPlots
|
||||||
|
// Reverse to chronological: oldest first, newest last
|
||||||
|
const chrono = [...plotList].reverse();
|
||||||
|
const lines = [];
|
||||||
|
chrono.forEach((p, idx) => {
|
||||||
|
lines.push(`【plot -${chrono.length - idx}】\n${p}`);
|
||||||
|
});
|
||||||
|
return `<previous_plots>\n${lines.join('\n\n')}\n</previous_plots>`;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* -------------------------
|
* -------------------------
|
||||||
* Worldbook — read via ST API (like idle-watcher)
|
* Worldbook — read via ST API (like idle-watcher)
|
||||||
@@ -1742,7 +1770,7 @@ async function buildPlannerMessages(rawUserInput) {
|
|||||||
// a little continuity even when memory recall returns empty.
|
// a little continuity even when memory recall returns empty.
|
||||||
const recentChatRaw = collectRecentChatSnippet(chat, 2);
|
const recentChatRaw = collectRecentChatSnippet(chat, 2);
|
||||||
|
|
||||||
const plotsRaw = readPlannerPlotHistory(chat, { count: s.plotCount }).block;
|
const plotsRaw = formatPlotsBlock(extractLastNPlots(chat, s.plotCount));
|
||||||
|
|
||||||
// Build scanText for worldbook keyword activation
|
// Build scanText for worldbook keyword activation
|
||||||
const scanText = [charBlockRaw, recentChatRaw, plotsRaw, rawUserInput].join('\n\n');
|
const scanText = [charBlockRaw, recentChatRaw, plotsRaw, rawUserInput].join('\n\n');
|
||||||
@@ -1913,22 +1941,22 @@ async function doInterceptAndPlanThenSend() {
|
|||||||
ta.value = `${raw}\n\n${preview}`.trim();
|
ta.value = `${raw}\n\n${preview}`.trim();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// Ordering requirement: write the merged textarea, register the
|
const merged = `${raw}\n\n${filtered}`.trim();
|
||||||
// one-shot planner recall handoff synchronously, then click send with
|
ta.value = merged;
|
||||||
// no await/timer hop in between.
|
state.lastInjectedText = merged;
|
||||||
applyPlannerResultAndSend({
|
|
||||||
textarea: ta,
|
// Ordering requirement: register the one-shot planner recall handoff
|
||||||
button: btn,
|
// synchronously before btn.click(), with no await/timer hop in between.
|
||||||
rawUserInput: raw,
|
if (_bmeRuntime?.preparePlannerRecallHandoff && plannerRecall?.result) {
|
||||||
filtered,
|
_bmeRuntime.preparePlannerRecallHandoff({
|
||||||
plannerRecall,
|
|
||||||
plannerPlotRecord: {
|
|
||||||
rawUserInput: raw,
|
rawUserInput: raw,
|
||||||
plotText: filtered,
|
plannerAugmentedMessage: merged,
|
||||||
},
|
plannerRecall,
|
||||||
runtime: _bmeRuntime,
|
});
|
||||||
plannerState: state,
|
}
|
||||||
});
|
|
||||||
|
state.bypassNextSend = true;
|
||||||
|
btn.click();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
ta.value = raw;
|
ta.value = raw;
|
||||||
state.lastInjectedText = '';
|
state.lastInjectedText = '';
|
||||||
|
|||||||
@@ -1,132 +0,0 @@
|
|||||||
import {
|
|
||||||
extractLastNPlots,
|
|
||||||
formatPlotsBlock,
|
|
||||||
} from './ena-planner-runtime-utils.js';
|
|
||||||
|
|
||||||
export const ST_BME_PLOT_HISTORY_KEY = 'st_bme_plot';
|
|
||||||
export const ST_BME_PLOT_HISTORY_VERSION = 1;
|
|
||||||
|
|
||||||
export function hashPlannerPlotInput(text = '') {
|
|
||||||
let hash = 2166136261;
|
|
||||||
for (const char of String(text || '')) {
|
|
||||||
hash ^= char.charCodeAt(0);
|
|
||||||
hash = Math.imul(hash, 16777619);
|
|
||||||
}
|
|
||||||
return String(Math.abs(hash >>> 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createStructuredPlotRecord({
|
|
||||||
rawUserInput = '',
|
|
||||||
plannerAugmentedMessage = '',
|
|
||||||
plotText = '',
|
|
||||||
plotBlocks = null,
|
|
||||||
promptProfileId = '',
|
|
||||||
recallHandoffId = '',
|
|
||||||
taskResults = [],
|
|
||||||
createdAt = Date.now(),
|
|
||||||
inputHash = '',
|
|
||||||
} = {}) {
|
|
||||||
const normalizedRaw = String(rawUserInput || '').trim();
|
|
||||||
const normalizedPlot = String(plotText || '').trim();
|
|
||||||
const blocks = Array.isArray(plotBlocks)
|
|
||||||
? plotBlocks.map((item) => String(item || '').trim()).filter(Boolean)
|
|
||||||
: extractLastNPlots([{ mes: normalizedPlot }], 99);
|
|
||||||
return {
|
|
||||||
version: ST_BME_PLOT_HISTORY_VERSION,
|
|
||||||
inputHash: String(inputHash || hashPlannerPlotInput(normalizedRaw)),
|
|
||||||
rawUserInput: normalizedRaw,
|
|
||||||
plannerAugmentedMessage: String(plannerAugmentedMessage || '').trim(),
|
|
||||||
plotText: normalizedPlot,
|
|
||||||
plotBlocks: blocks,
|
|
||||||
promptProfileId: String(promptProfileId || ''),
|
|
||||||
recallHandoffId: String(recallHandoffId || ''),
|
|
||||||
taskResults: Array.isArray(taskResults) ? taskResults : [],
|
|
||||||
createdAt: Number.isFinite(Number(createdAt)) ? Number(createdAt) : Date.now(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function normalizeStructuredPlotRecord(value) {
|
|
||||||
if (!value || typeof value !== 'object') return null;
|
|
||||||
if (Number(value.version) !== ST_BME_PLOT_HISTORY_VERSION) return null;
|
|
||||||
const plotText = String(value.plotText || '').trim();
|
|
||||||
const plotBlocks = Array.isArray(value.plotBlocks)
|
|
||||||
? value.plotBlocks.map((item) => String(item || '').trim()).filter(Boolean)
|
|
||||||
: [];
|
|
||||||
if (!plotText && plotBlocks.length === 0) return null;
|
|
||||||
return createStructuredPlotRecord({
|
|
||||||
...value,
|
|
||||||
plotText,
|
|
||||||
plotBlocks,
|
|
||||||
createdAt: value.createdAt,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function readStructuredPlotRecordFromMessage(message) {
|
|
||||||
return normalizeStructuredPlotRecord(message?.extra?.[ST_BME_PLOT_HISTORY_KEY]);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function collectStructuredPlotRecords(chat, count = 2) {
|
|
||||||
if (!Array.isArray(chat) || chat.length === 0) return [];
|
|
||||||
const want = Math.max(0, Number(count) || 0);
|
|
||||||
if (!want) return [];
|
|
||||||
const records = [];
|
|
||||||
for (let index = chat.length - 1; index >= 0; index--) {
|
|
||||||
const record = readStructuredPlotRecordFromMessage(chat[index]);
|
|
||||||
if (!record) continue;
|
|
||||||
records.push(record);
|
|
||||||
if (records.length >= want) break;
|
|
||||||
}
|
|
||||||
return records;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function readPlannerPlotHistory(chat, { count = 2 } = {}) {
|
|
||||||
const structuredRecords = collectStructuredPlotRecords(chat, count);
|
|
||||||
if (structuredRecords.length > 0) {
|
|
||||||
const plots = structuredRecords.map((record) => record.plotText || record.plotBlocks.join('\n')).filter(Boolean);
|
|
||||||
return {
|
|
||||||
source: 'structured',
|
|
||||||
records: structuredRecords,
|
|
||||||
plots,
|
|
||||||
block: formatPlotsBlock(plots),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const plots = extractLastNPlots(chat, count);
|
|
||||||
return {
|
|
||||||
source: plots.length > 0 ? 'legacy' : 'empty',
|
|
||||||
records: [],
|
|
||||||
plots,
|
|
||||||
block: formatPlotsBlock(plots),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function writeStructuredPlotRecordToMessage(message, recordInput) {
|
|
||||||
if (!message || typeof message !== 'object' || !message.is_user) return false;
|
|
||||||
const record = normalizeStructuredPlotRecord(
|
|
||||||
recordInput?.version ? recordInput : createStructuredPlotRecord(recordInput),
|
|
||||||
);
|
|
||||||
if (!record) return false;
|
|
||||||
message.extra = message.extra && typeof message.extra === 'object'
|
|
||||||
? message.extra
|
|
||||||
: {};
|
|
||||||
message.extra[ST_BME_PLOT_HISTORY_KEY] = record;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function writeStructuredPlotRecordToMatchingUserMessage(chat, recordInput) {
|
|
||||||
if (!Array.isArray(chat)) return null;
|
|
||||||
const record = normalizeStructuredPlotRecord(
|
|
||||||
recordInput?.version ? recordInput : createStructuredPlotRecord(recordInput),
|
|
||||||
);
|
|
||||||
if (!record) return null;
|
|
||||||
const inputHash = String(record.inputHash || hashPlannerPlotInput(record.rawUserInput));
|
|
||||||
for (let index = chat.length - 1; index >= 0; index--) {
|
|
||||||
const message = chat[index];
|
|
||||||
if (!message?.is_user) continue;
|
|
||||||
if (hashPlannerPlotInput(message.mes || '') !== inputHash) continue;
|
|
||||||
if (writeStructuredPlotRecordToMessage(message, record)) {
|
|
||||||
return { index, record };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
@@ -327,7 +327,6 @@ export function onMessageSentController(runtime, messageId) {
|
|||||||
resolvedMessageId,
|
resolvedMessageId,
|
||||||
message.mes || "",
|
message.mes || "",
|
||||||
);
|
);
|
||||||
runtime.persistPlannerPlotRecordToUserMessage?.(resolvedMessageId);
|
|
||||||
// GENERATION_AFTER_COMMANDS 在 sendMessageAsUser 之前触发,此时新用户消息
|
// GENERATION_AFTER_COMMANDS 在 sendMessageAsUser 之前触发,此时新用户消息
|
||||||
// 尚未进入 chat,recall 记录会被写到上一条 user 上。这里用户消息刚入场,
|
// 尚未进入 chat,recall 记录会被写到上一条 user 上。这里用户消息刚入场,
|
||||||
// transaction 仍在桥接窗口内,立即把记录重新绑定到正确的楼层。
|
// transaction 仍在桥接窗口内,立即把记录重新绑定到正确的楼层。
|
||||||
|
|||||||
29
index.js
29
index.js
@@ -111,7 +111,6 @@ import {
|
|||||||
registerGenerationAfterCommandsController,
|
registerGenerationAfterCommandsController,
|
||||||
scheduleSendIntentHookRetryController,
|
scheduleSendIntentHookRetryController,
|
||||||
} from "./host/event-binding.js";
|
} from "./host/event-binding.js";
|
||||||
import { writeStructuredPlotRecordToMessage } from "./ena-planner/planner-plot-history.js";
|
|
||||||
import {
|
import {
|
||||||
BME_HOST_PROFILE_LUKER,
|
BME_HOST_PROFILE_LUKER,
|
||||||
getBmeHostAdapter,
|
getBmeHostAdapter,
|
||||||
@@ -14735,43 +14734,16 @@ function preparePlannerRecallHandoff({
|
|||||||
rawUserInput = "",
|
rawUserInput = "",
|
||||||
plannerAugmentedMessage = "",
|
plannerAugmentedMessage = "",
|
||||||
plannerRecall = null,
|
plannerRecall = null,
|
||||||
plannerPlotRecord = null,
|
|
||||||
chatId = getCurrentChatId(),
|
chatId = getCurrentChatId(),
|
||||||
} = {}) {
|
} = {}) {
|
||||||
return rerollRecallInput.preparePlannerRecallHandoff({
|
return rerollRecallInput.preparePlannerRecallHandoff({
|
||||||
rawUserInput,
|
rawUserInput,
|
||||||
plannerAugmentedMessage,
|
plannerAugmentedMessage,
|
||||||
plannerRecall,
|
plannerRecall,
|
||||||
plannerPlotRecord,
|
|
||||||
chatId,
|
chatId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function persistPlannerPlotRecordToUserMessage(newUserMessageIndex) {
|
|
||||||
const context = getContext();
|
|
||||||
const chat = context?.chat;
|
|
||||||
if (
|
|
||||||
!Array.isArray(chat) ||
|
|
||||||
!Number.isFinite(newUserMessageIndex) ||
|
|
||||||
!chat[newUserMessageIndex]?.is_user
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const handoff = peekPlannerRecallHandoff(context?.chatId || getCurrentChatId());
|
|
||||||
const plannerPlotRecord = handoff?.plannerPlotRecord;
|
|
||||||
if (!plannerPlotRecord || typeof plannerPlotRecord !== "object") {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const wrote = writeStructuredPlotRecordToMessage(chat[newUserMessageIndex], {
|
|
||||||
...plannerPlotRecord,
|
|
||||||
recallHandoffId: handoff.id || plannerPlotRecord.recallHandoffId || "",
|
|
||||||
});
|
|
||||||
if (wrote) {
|
|
||||||
triggerChatMetadataSave(context, { immediate: false });
|
|
||||||
}
|
|
||||||
return wrote;
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildPreGenerationRecallKey(type, options = {}) {
|
function buildPreGenerationRecallKey(type, options = {}) {
|
||||||
return generationRecallTransactionRuntime.buildPreGenerationRecallKey(
|
return generationRecallTransactionRuntime.buildPreGenerationRecallKey(
|
||||||
type,
|
type,
|
||||||
@@ -16079,7 +16051,6 @@ function onMessageSent(messageId) {
|
|||||||
getContext,
|
getContext,
|
||||||
isTrivialUserInput,
|
isTrivialUserInput,
|
||||||
markCurrentGenerationTrivialSkip,
|
markCurrentGenerationTrivialSkip,
|
||||||
persistPlannerPlotRecordToUserMessage,
|
|
||||||
recordRecallSentUserMessage,
|
recordRecallSentUserMessage,
|
||||||
rebindRecallRecordToNewUserMessage,
|
rebindRecallRecordToNewUserMessage,
|
||||||
refreshPersistedRecallMessageUi: schedulePersistedRecallMessageUiRefresh,
|
refreshPersistedRecallMessageUi: schedulePersistedRecallMessageUiRefresh,
|
||||||
|
|||||||
@@ -6,6 +6,6 @@
|
|||||||
"js": "index.js",
|
"js": "index.js",
|
||||||
"css": "style.css",
|
"css": "style.css",
|
||||||
"author": "Youzini",
|
"author": "Youzini",
|
||||||
"version": "7.6.6",
|
"version": "7.6.4",
|
||||||
"homePage": "https://github.com/Youzini-afk/ST-Bionic-Memory-Ecology"
|
"homePage": "https://github.com/Youzini-afk/ST-Bionic-Memory-Ecology"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -309,7 +309,6 @@ export function createRerollRecallInput(deps = {}) {
|
|||||||
rawUserInput = "",
|
rawUserInput = "",
|
||||||
plannerAugmentedMessage = "",
|
plannerAugmentedMessage = "",
|
||||||
plannerRecall = null,
|
plannerRecall = null,
|
||||||
plannerPlotRecord = null,
|
|
||||||
chatId = getCurrentChatId(),
|
chatId = getCurrentChatId(),
|
||||||
} = {}) {
|
} = {}) {
|
||||||
const normalizedChatId = normalizeChatIdCandidate(chatId);
|
const normalizedChatId = normalizeChatIdCandidate(chatId);
|
||||||
@@ -341,10 +340,6 @@ export function createRerollRecallInput(deps = {}) {
|
|||||||
? plannerRecall.recentMessages.map((item) => String(item || ""))
|
? plannerRecall.recentMessages.map((item) => String(item || ""))
|
||||||
: [],
|
: [],
|
||||||
injectionText,
|
injectionText,
|
||||||
plannerPlotRecord:
|
|
||||||
plannerPlotRecord && typeof plannerPlotRecord === "object"
|
|
||||||
? { ...plannerPlotRecord }
|
|
||||||
: null,
|
|
||||||
source: "planner-handoff",
|
source: "planner-handoff",
|
||||||
sourceLabel: "Planner handoff",
|
sourceLabel: "Planner handoff",
|
||||||
createdAt,
|
createdAt,
|
||||||
|
|||||||
@@ -1,135 +0,0 @@
|
|||||||
import assert from 'node:assert/strict';
|
|
||||||
|
|
||||||
import {
|
|
||||||
applyPlannerResultAndSend,
|
|
||||||
extractLastNPlots,
|
|
||||||
formatPlotsBlock,
|
|
||||||
} from '../ena-planner/ena-planner-runtime-utils.js';
|
|
||||||
import {
|
|
||||||
createStructuredPlotRecord,
|
|
||||||
readPlannerPlotHistory,
|
|
||||||
writeStructuredPlotRecordToMatchingUserMessage,
|
|
||||||
writeStructuredPlotRecordToMessage,
|
|
||||||
} from '../ena-planner/planner-plot-history.js';
|
|
||||||
|
|
||||||
{
|
|
||||||
const chat = [
|
|
||||||
{ mes: 'no plot here' },
|
|
||||||
{ mes: '<plot>old one</plot>\n<plot>old two</plot>' },
|
|
||||||
{ mes: 'assistant says <plot>new one</plot>' },
|
|
||||||
];
|
|
||||||
assert.deepEqual(extractLastNPlots(chat, 2), [
|
|
||||||
'<plot>new one</plot>',
|
|
||||||
'<plot>old two</plot>',
|
|
||||||
]);
|
|
||||||
assert.deepEqual(extractLastNPlots(chat, 0), []);
|
|
||||||
assert.deepEqual(extractLastNPlots(null, 3), []);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
const block = formatPlotsBlock([
|
|
||||||
'<plot>newest</plot>',
|
|
||||||
'<plot>older</plot>',
|
|
||||||
]);
|
|
||||||
assert.equal(
|
|
||||||
block,
|
|
||||||
'<previous_plots>\n【plot -2】\n<plot>older</plot>\n\n【plot -1】\n<plot>newest</plot>\n</previous_plots>',
|
|
||||||
);
|
|
||||||
assert.equal(formatPlotsBlock([]), '');
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
const order = [];
|
|
||||||
const textarea = { value: 'raw' };
|
|
||||||
const button = { click: () => order.push('click') };
|
|
||||||
const plannerState = { bypassNextSend: false, lastInjectedText: '' };
|
|
||||||
const plannerRecall = { result: { selected: ['memory-a'] } };
|
|
||||||
const runtime = {
|
|
||||||
preparePlannerRecallHandoff(payload) {
|
|
||||||
order.push('handoff');
|
|
||||||
assert.equal(payload.rawUserInput, 'raw input');
|
|
||||||
assert.equal(payload.plannerAugmentedMessage, 'raw input\n\n<plot>next</plot>');
|
|
||||||
assert.equal(payload.plannerRecall, plannerRecall);
|
|
||||||
assert.deepEqual(payload.plannerPlotRecord, {
|
|
||||||
rawUserInput: 'raw input',
|
|
||||||
plannerAugmentedMessage: 'raw input\n\n<plot>next</plot>',
|
|
||||||
plotText: '<plot>next</plot>',
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const result = applyPlannerResultAndSend({
|
|
||||||
textarea,
|
|
||||||
button,
|
|
||||||
rawUserInput: 'raw input',
|
|
||||||
filtered: '<plot>next</plot>',
|
|
||||||
plannerRecall,
|
|
||||||
plannerPlotRecord: { plotText: '<plot>next</plot>' },
|
|
||||||
runtime,
|
|
||||||
plannerState,
|
|
||||||
});
|
|
||||||
|
|
||||||
assert.deepEqual(order, ['handoff', 'click']);
|
|
||||||
assert.equal(result.applied, true);
|
|
||||||
assert.equal(result.handoffPrepared, true);
|
|
||||||
assert.equal(textarea.value, 'raw input\n\n<plot>next</plot>');
|
|
||||||
assert.equal(plannerState.lastInjectedText, textarea.value);
|
|
||||||
assert.equal(plannerState.bypassNextSend, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
const chat = [
|
|
||||||
{ is_user: true, mes: 'raw old', extra: {} },
|
|
||||||
{ is_user: false, mes: '<plot>legacy stale</plot>' },
|
|
||||||
{ is_user: true, mes: 'raw latest', extra: {} },
|
|
||||||
];
|
|
||||||
writeStructuredPlotRecordToMessage(chat[2], createStructuredPlotRecord({
|
|
||||||
rawUserInput: 'raw latest',
|
|
||||||
plannerAugmentedMessage: 'raw latest\n\n<plot>structured</plot>',
|
|
||||||
plotText: '<plot>structured</plot>',
|
|
||||||
}));
|
|
||||||
const history = readPlannerPlotHistory(chat, { count: 2 });
|
|
||||||
assert.equal(history.source, 'structured');
|
|
||||||
assert.deepEqual(history.plots, ['<plot>structured</plot>']);
|
|
||||||
assert.ok(history.block.includes('<plot>structured</plot>'));
|
|
||||||
assert.ok(!history.block.includes('legacy stale'));
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
const chat = [
|
|
||||||
{ is_user: true, mes: 'raw old', extra: {} },
|
|
||||||
{ is_user: false, mes: '<plot>legacy old</plot>' },
|
|
||||||
];
|
|
||||||
chat[0].extra.st_bme_plot = { version: 999, plotText: '<plot>bad</plot>' };
|
|
||||||
const history = readPlannerPlotHistory(chat, { count: 1 });
|
|
||||||
assert.equal(history.source, 'legacy');
|
|
||||||
assert.deepEqual(history.plots, ['<plot>legacy old</plot>']);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
const chat = [
|
|
||||||
{ is_user: true, mes: 'first input', extra: {} },
|
|
||||||
{ is_user: false, mes: 'assistant' },
|
|
||||||
{ is_user: true, mes: 'second input', extra: {} },
|
|
||||||
];
|
|
||||||
const result = writeStructuredPlotRecordToMatchingUserMessage(chat, {
|
|
||||||
rawUserInput: 'first input',
|
|
||||||
plannerAugmentedMessage: 'first input\n\n<plot>first plan</plot>',
|
|
||||||
plotText: '<plot>first plan</plot>',
|
|
||||||
});
|
|
||||||
assert.equal(result.index, 0);
|
|
||||||
assert.equal(chat[0].extra.st_bme_plot.plotText, '<plot>first plan</plot>');
|
|
||||||
assert.equal(chat[2].extra.st_bme_plot, undefined);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
const order = [];
|
|
||||||
const result = applyPlannerResultAndSend({
|
|
||||||
textarea: null,
|
|
||||||
button: { click: () => order.push('click') },
|
|
||||||
});
|
|
||||||
assert.deepEqual(result, { applied: false, reason: 'missing-target' });
|
|
||||||
assert.deepEqual(order, []);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('ena-planner-plots tests passed');
|
|
||||||
Reference in New Issue
Block a user