Add debug log toggle and silence diagnostic logs

This commit is contained in:
Youzini-afk
2026-04-06 11:26:37 +08:00
parent b6187d46e0
commit 8320e3e7f6
23 changed files with 226 additions and 96 deletions

View File

@@ -1,6 +1,7 @@
// ST-BME: 层级压缩引擎
// 超过阈值的节点被 LLM 总结为更高层级的压缩节点
import { debugLog } from "./debug-logging.js";
import { embedText } from "./embedding.js";
import {
addEdge,
@@ -517,7 +518,7 @@ export function sleepCycle(graph, settings) {
}
if (forgotten > 0) {
console.log(`[ST-BME] 主动遗忘: ${forgotten} 个低价值节点已归档`);
debugLog(`[ST-BME] 主动遗忘: ${forgotten} 个低价值节点已归档`);
}
return { forgotten };

View File

@@ -2,6 +2,7 @@
// 合并 Mem0 精确对照 + A-MEM 记忆进化为单一阶段
// 批量 embed + 批量查近邻 + 单次 LLM 调用
import { debugLog } from "./debug-logging.js";
import { embedBatch, searchSimilar } from "./embedding.js";
import { addEdge, createEdge, getActiveNodes, getNode } from "./graph.js";
import { callLLMForJSON } from "./llm.js";
@@ -301,7 +302,7 @@ export async function consolidateMemories({
if (!newNodeIds || newNodeIds.length === 0) return stats;
if (!validateVectorConfig(embeddingConfig).valid) {
console.log("[ST-BME] 记忆整合跳过:向量配置不可用");
debugLog("[ST-BME] 记忆整合跳过:向量配置不可用");
return stats;
}
@@ -331,7 +332,7 @@ export async function consolidateMemories({
}
throwIfAborted(signal);
console.log(`[ST-BME] 记忆整合开始: ${newEntries.length} 个新节点`);
debugLog(`[ST-BME] 记忆整合开始: ${newEntries.length} 个新节点`);
// ══════════════════════════════════════════════
// Phase 1 + 2: 批量 Embed + 查近邻
@@ -571,7 +572,7 @@ export async function consolidateMemories({
if (stats.updates > 0) actionSummary.push(`回溯更新 ${stats.updates}`);
if (actionSummary.length > 0) {
console.log(`[ST-BME] 记忆整合完成: ${actionSummary.join(", ")}`);
debugLog(`[ST-BME] 记忆整合完成: ${actionSummary.join(", ")}`);
}
return stats;
@@ -586,7 +587,7 @@ function processOneResult(graph, entry, result, stats) {
// ── 处理 action ──
switch (result.action) {
case "skip": {
console.log(`[ST-BME] 记忆整合: skip (重复) — ${newId}`);
debugLog(`[ST-BME] 记忆整合: skip (重复) — ${newId}`);
newNode.archived = true;
stats.skipped++;
break;
@@ -601,7 +602,7 @@ function processOneResult(graph, entry, result, stats) {
!targetNode.archived &&
canMergeScopedMemories(newNode, targetNode)
) {
console.log(`[ST-BME] 记忆整合: merge ${newId}${targetId}`);
debugLog(`[ST-BME] 记忆整合: merge ${newId}${targetId}`);
if (result.merged_fields && typeof result.merged_fields === "object") {
for (const [key, value] of Object.entries(result.merged_fields)) {
@@ -658,7 +659,7 @@ function processOneResult(graph, entry, result, stats) {
const evolution = result.evolution;
if (evolution?.should_evolve && !newNode.archived) {
stats.evolved++;
console.log(`[ST-BME] 记忆整合/进化触发: ${result.reason || "(无理由)"}`);
debugLog(`[ST-BME] 记忆整合/进化触发: ${result.reason || "(无理由)"}`);
if (Array.isArray(evolution.connections)) {
for (const targetId of evolution.connections) {

59
debug-logging.js Normal file
View File

@@ -0,0 +1,59 @@
const MODULE_NAME = "st_bme";
const GLOBAL_DEBUG_FLAG_KEY = "__stBmeDebugLoggingEnabled";
function resolveModuleSettings(settings = null) {
if (settings && typeof settings === "object") {
return settings;
}
const moduleSettings =
globalThis?.extension_settings?.[MODULE_NAME] ||
globalThis?.__p0ExtensionSettings?.[MODULE_NAME] ||
globalThis?.SillyTavern?.getContext?.()?.extensionSettings?.[MODULE_NAME] ||
null;
return moduleSettings && typeof moduleSettings === "object"
? moduleSettings
: null;
}
export function isDebugLoggingEnabled(settings = null) {
if (
settings &&
typeof settings === "object" &&
Object.prototype.hasOwnProperty.call(settings, "debugLoggingEnabled")
) {
return Boolean(settings.debugLoggingEnabled);
}
if (typeof globalThis[GLOBAL_DEBUG_FLAG_KEY] === "boolean") {
return globalThis[GLOBAL_DEBUG_FLAG_KEY];
}
return Boolean(resolveModuleSettings(settings)?.debugLoggingEnabled);
}
function emitDebugLog(method, args, settings = null) {
if (!isDebugLoggingEnabled(settings)) {
return;
}
const target =
typeof console?.[method] === "function" ? console[method] : console.log;
Reflect.apply(target, console, args);
}
export function debugLog(...args) {
emitDebugLog("log", args);
}
export function debugInfo(...args) {
emitDebugLog("info", args);
}
export function debugWarn(...args) {
emitDebugLog("warn", args);
}
export function debugDebug(...args) {
emitDebugLog("debug", args);
}

View File

@@ -2,6 +2,7 @@ import { extension_settings } from '../../../../extensions.js';
import { getRequestHeaders, saveSettingsDebounced, substituteParamsExtended } from '../../../../../script.js';
import { EnaPlannerStorage, migrateFromLWBIfNeeded } from './ena-planner-storage.js';
import { DEFAULT_PROMPT_BLOCKS, BUILTIN_TEMPLATES } from './ena-planner-presets.js';
import { debugLog } from '../debug-logging.js';
import jsyaml from '../vendor/js-yaml.mjs';
const EXT_NAME = 'ena-planner';
@@ -199,7 +200,7 @@ async function saveConfigNow() {
function toastInfo(msg) {
if (window.toastr?.info) return window.toastr.info(msg);
console.log('[EnaPlanner]', msg);
debugLog('[EnaPlanner]', msg);
}
function toastErr(msg) {
if (window.toastr?.error) return window.toastr.error(msg);
@@ -505,7 +506,7 @@ async function getCharacterWorldbooks() {
}
} catch { }
console.log('[EnaPlanner] Character worldbook names found:', worldNames);
debugLog('[EnaPlanner] Character worldbook names found:', worldNames);
return worldNames.filter(Boolean);
}
@@ -618,11 +619,11 @@ async function buildWorldbookBlock(scanText) {
const allWorldNames = [...new Set([...charWorldNames, ...globalWorldNames])];
if (!allWorldNames.length) {
console.log('[EnaPlanner] No worldbooks to load');
debugLog('[EnaPlanner] No worldbooks to load');
return '';
}
console.log('[EnaPlanner] Loading worldbooks:', allWorldNames);
debugLog('[EnaPlanner] Loading worldbooks:', allWorldNames);
// Fetch all worldbook data
const worldbookResults = await Promise.all(allWorldNames.map(name => getWorldbookData(name)));
@@ -1155,7 +1156,7 @@ async function buildPlannerMessages(rawUserInput) {
clearTimeout(timeoutId);
}
}
console.log(`[Ena] Memory source: ${memorySource}`);
debugLog(`[Ena] Memory source: ${memorySource}`);
// --- Chat history: last 2 AI messages (floors N-1 & N-3) ---
// Two messages instead of one to avoid cross-device cache miss:

View File

@@ -1,3 +1,5 @@
import { debugWarn } from "./debug-logging.js";
function getTimerApi(runtime) {
const rawSetTimeout =
typeof runtime?.setTimeout === "function"
@@ -38,14 +40,14 @@ export function registerBeforeCombinePromptsController(runtime, listener) {
export function registerGenerationAfterCommandsController(runtime, listener) {
const makeFirst = runtime.getEventMakeFirst();
const eventName = runtime.eventTypes.GENERATION_AFTER_COMMANDS;
console.warn("[ST-BME:DIAG] Registering GENERATION_AFTER_COMMANDS:", {
debugWarn("[ST-BME:DIAG] Registering GENERATION_AFTER_COMMANDS:", {
eventName,
hasMakeFirst: typeof makeFirst === "function",
hasListener: typeof listener === "function",
});
if (typeof makeFirst === "function") {
const cleanup = makeFirst(eventName, listener);
console.warn("[ST-BME:DIAG] Registered via makeFirst, cleanup:", typeof cleanup);
debugWarn("[ST-BME:DIAG] Registered via makeFirst, cleanup:", typeof cleanup);
return cleanup;
}
@@ -406,9 +408,9 @@ export async function onGenerationAfterCommandsController(
params = {},
dryRun = false,
) {
console.warn("[ST-BME:DIAG] GENERATION_AFTER_COMMANDS fired", { type, dryRun, paramsKeys: Object.keys(params || {}) });
debugWarn("[ST-BME:DIAG] GENERATION_AFTER_COMMANDS fired", { type, dryRun, paramsKeys: Object.keys(params || {}) });
if (dryRun) {
console.warn("[ST-BME:DIAG] EXIT: dryRun=true");
debugWarn("[ST-BME:DIAG] EXIT: dryRun=true");
return;
}
@@ -418,11 +420,11 @@ export async function onGenerationAfterCommandsController(
? runtime.consumeHostGenerationInputSnapshot?.({ preserve: true }) ||
runtime.consumeHostGenerationInputSnapshot?.()
: null;
console.warn("[ST-BME:DIAG] frozenInputSnapshot:", frozenInputSnapshot?.text ? `"${frozenInputSnapshot.text.slice(0,50)}"` : "(empty)", "fresh:", !!frozenInputSnapshot?.at);
debugWarn("[ST-BME:DIAG] frozenInputSnapshot:", frozenInputSnapshot?.text ? `"${frozenInputSnapshot.text.slice(0,50)}"` : "(empty)", "fresh:", !!frozenInputSnapshot?.at);
const context = runtime.getContext();
const chat = context?.chat;
console.warn("[ST-BME:DIAG] chat length:", chat?.length, "last msg:", chat?.length ? { is_user: chat[chat.length-1]?.is_user, mes: (chat[chat.length-1]?.mes||"").slice(0,50) } : "(no chat)");
debugWarn("[ST-BME:DIAG] chat length:", chat?.length, "last msg:", chat?.length ? { is_user: chat[chat.length-1]?.is_user, mes: (chat[chat.length-1]?.mes||"").slice(0,50) } : "(no chat)");
const recallOptions = runtime.buildGenerationAfterCommandsRecallInput(
type,
@@ -433,14 +435,14 @@ export async function onGenerationAfterCommandsController(
chat,
);
if (!recallOptions) {
console.warn("[ST-BME:DIAG] EXIT: buildGenerationAfterCommandsRecallInput returned null");
debugWarn("[ST-BME:DIAG] EXIT: buildGenerationAfterCommandsRecallInput returned null");
return;
}
if (recallOptions?.__trivialSkip) {
console.warn("[ST-BME:DIAG] EXIT: trivial-input-skip");
debugWarn("[ST-BME:DIAG] EXIT: trivial-input-skip");
return;
}
console.warn("[ST-BME:DIAG] recallOptions:", { generationType: recallOptions.generationType, overrideUserMessage: recallOptions.overrideUserMessage?.slice(0,50), overrideSource: recallOptions.overrideSource, targetIdx: recallOptions.targetUserMessageIndex });
debugWarn("[ST-BME:DIAG] recallOptions:", { generationType: recallOptions.generationType, overrideUserMessage: recallOptions.overrideUserMessage?.slice(0,50), overrideSource: recallOptions.overrideSource, targetIdx: recallOptions.targetUserMessageIndex });
const recallContext = runtime.createGenerationRecallContext({
hookName: "GENERATION_AFTER_COMMANDS",
@@ -448,10 +450,10 @@ export async function onGenerationAfterCommandsController(
recallOptions,
});
if (!recallContext.shouldRun && !recallContext.transaction) {
console.warn("[ST-BME:DIAG] EXIT: shouldRun=false, no transaction. guardReason:", recallContext.guardReason);
debugWarn("[ST-BME:DIAG] EXIT: shouldRun=false, no transaction. guardReason:", recallContext.guardReason);
return;
}
console.warn("[ST-BME:DIAG] recallContext:", { shouldRun: recallContext.shouldRun, guardReason: recallContext.guardReason, transactionId: recallContext.transaction?.id });
debugWarn("[ST-BME:DIAG] recallContext:", { shouldRun: recallContext.shouldRun, guardReason: recallContext.guardReason, transactionId: recallContext.transaction?.id });
const runtimeRecallOptions =
recallContext.recallOptions || recallOptions || {};
@@ -464,7 +466,7 @@ export async function onGenerationAfterCommandsController(
let recallResult = runtime.getGenerationRecallTransactionResult?.(
recallContext.transaction,
);
console.warn("[ST-BME:DIAG] deliveryMode:", deliveryMode, "shouldRun:", recallContext.shouldRun);
debugWarn("[ST-BME:DIAG] deliveryMode:", deliveryMode, "shouldRun:", recallContext.shouldRun);
if (recallContext.shouldRun) {
runtime.markGenerationRecallTransactionHookState(
@@ -475,7 +477,7 @@ export async function onGenerationAfterCommandsController(
if (deliveryMode === "deferred") {
runtime.clearLiveRecallInjectionPromptForRewrite?.();
}
console.warn("[ST-BME:DIAG] >>> Starting runRecall...");
debugWarn("[ST-BME:DIAG] >>> Starting runRecall...");
recallResult = await runtime.runRecall({
...runtimeRecallOptions,
deliveryMode,
@@ -483,7 +485,7 @@ export async function onGenerationAfterCommandsController(
hookName: recallContext.hookName,
signal: params?.signal,
});
console.warn("[ST-BME:DIAG] <<< runRecall finished:", { status: recallResult?.status, ok: recallResult?.ok, reason: recallResult?.reason, injectionText: recallResult?.injectionText?.slice(0,80) });
debugWarn("[ST-BME:DIAG] <<< runRecall finished:", { status: recallResult?.status, ok: recallResult?.ok, reason: recallResult?.reason, injectionText: recallResult?.injectionText?.slice(0,80) });
runtime.storeGenerationRecallTransactionResult?.(
recallContext.transaction,
recallResult,
@@ -516,7 +518,7 @@ export async function onGenerationAfterCommandsController(
// 上面的兜底补写会把 fresh recall 绑定回最终 user 楼层。
// 这里再补一次 UI 刷新,避免需要等到消息编辑/历史恢复后才看到 Recall Card。
runtime.refreshPersistedRecallMessageUi?.();
console.warn("[ST-BME:DIAG] DONE: immediate mode, injection via setExtensionPrompt in runRecall");
debugWarn("[ST-BME:DIAG] DONE: immediate mode, injection via setExtensionPrompt in runRecall");
return recallResult;
}
@@ -550,7 +552,7 @@ export async function onBeforeCombinePromptsController(
frozenInputSnapshot,
});
if (normalInput?.__trivialSkip) {
console.warn("[ST-BME:DIAG] EXIT: trivial-input-skip");
debugWarn("[ST-BME:DIAG] EXIT: trivial-input-skip");
return {
skipped: true,
reason: `trivial:${normalInput.trivialReason || ""}`,

View File

@@ -1,6 +1,8 @@
// ST-BME: 提取编排控制器(纯函数)
// 通过 runtime 依赖注入,避免直接访问 index.js 模块级状态。
import { debugLog } from "./debug-logging.js";
export async function executeExtractionBatchController(
runtime,
{
@@ -25,7 +27,7 @@ export async function executeExtractionBatchController(
extractionCountBefore,
});
runtime.console.log(
debugLog(
`[ST-BME] 开始提取: 楼层 ${startIdx}-${endIdx}` +
(smartTriggerDecision?.triggered
? ` [智能触发 score=${smartTriggerDecision.score}; ${smartTriggerDecision.reasons.join(" / ")}]`
@@ -504,7 +506,7 @@ export async function onRerollController(runtime, { fromFloor } = {}) {
};
}
runtime.console.log(`[ST-BME] 重 Roll 开始,目标楼层: ${targetFloor}`);
debugLog(`[ST-BME] 重 Roll 开始,目标楼层: ${targetFloor}`);
let rollbackResult;
try {
rollbackResult = await runtime.rollbackGraphForReroll(targetFloor, context);

View File

@@ -3,6 +3,7 @@
// v2: 融合 Mem0 精确对照 + Graphiti 时序边 + MemoRAG 全局概要
import { embedBatch } from "./embedding.js";
import { debugLog, debugWarn } from "./debug-logging.js";
import {
addEdge,
addNode,
@@ -345,7 +346,7 @@ export async function extractMemories({
activeUserOwner: stContext?.prompt?.userName || "",
};
console.log(
debugLog(
`[ST-BME] 提取开始: chat[${effectiveStartSeq}..${effectiveEndSeq}], ${messages.length} 条消息`,
);
@@ -416,7 +417,7 @@ export async function extractMemories({
const pm = Array.isArray(promptPayload.promptMessages) ? promptPayload.promptMessages : [];
const pmUser = pm.filter((m) => m?.role === "user");
const am = Array.isArray(promptPayload.additionalMessages) ? promptPayload.additionalMessages : [];
console.log(
debugLog(
`[ST-BME][prompt-diag] resolveTaskPromptPayload: ` +
`promptMessages=${pm.length} (user=${pmUser.length}), ` +
`additionalMessages=${am.length}, ` +
@@ -426,13 +427,13 @@ export async function extractMemories({
);
if (pmUser.length > 0) {
for (const m of pmUser) {
console.log(
debugLog(
`[ST-BME][prompt-diag] user msg: contentLen=${String(m.content || "").length}, ` +
`blockName="${m.blockName || ""}", preview="${String(m.content || "").slice(0, 60)}..."`,
);
}
} else {
console.warn(
debugWarn(
`[ST-BME][prompt-diag] NO user messages in promptMessages! Fallback userPrompt will be used.`,
);
}
@@ -537,7 +538,7 @@ export async function extractMemories({
);
updateRuntimeScopeState(graph, newNodeIds, scopeRuntime);
console.log(
debugLog(
`[ST-BME] 提取完成: 新建 ${stats.newNodes}, 更新 ${stats.updatedNodes}, 新边 ${stats.newEdges}, lastProcessedSeq=${graph.lastProcessedSeq}`,
);
@@ -863,7 +864,7 @@ async function generateNodeEmbeddings(graph, embeddingConfig, signal) {
(node) => buildNodeVectorText(node) || node.type,
);
console.log(`[ST-BME] 为 ${texts.length} 个节点生成 embedding`);
debugLog(`[ST-BME] 为 ${texts.length} 个节点生成 embedding`);
const embeddings = await embedBatch(texts, embeddingConfig, { signal });
@@ -1089,7 +1090,7 @@ export async function generateSynopsis({
Math.max(existingSynopsis.seqRange?.[1] ?? currentSeq, currentSeq),
];
existingSynopsis.embedding = null;
console.log("[ST-BME] 全局概要已更新");
debugLog("[ST-BME] 全局概要已更新");
} else {
const node = createNode({
type: "synopsis",
@@ -1098,7 +1099,7 @@ export async function generateSynopsis({
importance: 9.0,
});
addNode(graph, node);
console.log("[ST-BME] 全局概要已创建");
debugLog("[ST-BME] 全局概要已创建");
}
}
@@ -1238,6 +1239,6 @@ export async function generateReflection({
addEdge(graph, edge);
}
console.log("[ST-BME] 反思条目已生成");
debugLog("[ST-BME] 反思条目已生成");
return reflectionNode.id;
}

View File

@@ -14,6 +14,7 @@ import {
normalizeNodeMemoryScope,
isSameLatestScopeBucket,
} from "./memory-scope.js";
import { debugLog } from "./debug-logging.js";
/**
* 图状态版本号
@@ -546,7 +547,7 @@ export function deserializeGraph(json) {
}
if (data.version < GRAPH_VERSION) {
console.log(`[ST-BME] 图版本迁移 v${data.version} → v${GRAPH_VERSION}`);
debugLog(`[ST-BME] 图版本迁移 v${data.version} → v${GRAPH_VERSION}`);
if (data.version < 2 && data.edges) {
for (const edge of data.edges) {

View File

@@ -1,6 +1,7 @@
import { getContext as extensionGetContext } from "../../../../extensions.js";
import { buildCapabilityStatus, mergeVersionHints } from "./capabilities.js";
import { debugDebug } from "../debug-logging.js";
function resolveContextGetter(providedGetter = null) {
if (typeof providedGetter === "function") {
@@ -55,7 +56,7 @@ export function createContextHostFacade(options = {}) {
try {
return getContext(...args);
} catch (error) {
console.debug(
debugDebug(
"[ST-BME] host-adapter/context getContext 调用失败",
error,
);
@@ -71,7 +72,7 @@ export function createContextHostFacade(options = {}) {
const context = getContext(...args);
return context && typeof context === "object" ? context : null;
} catch (error) {
console.debug("[ST-BME] host-adapter/context 读取上下文失败", error);
debugDebug("[ST-BME] host-adapter/context 读取上下文失败", error);
return null;
}
},

View File

@@ -1,5 +1,6 @@
import { buildCapabilityStatus, mergeVersionHints } from "./capabilities.js";
import { createContextHostFacade } from "./context.js";
import { debugDebug } from "../debug-logging.js";
function resolvePromptSetter(providedSetter = null, contextHost = null) {
if (typeof providedSetter === "function") {
@@ -75,7 +76,7 @@ export function createInjectionHostFacade(options = {}) {
liveSetterRecord.setter(...args);
return true;
} catch (error) {
console.debug(
debugDebug(
"[ST-BME] host-adapter/injection setExtensionPrompt 调用失败",
error,
);

View File

@@ -1,5 +1,6 @@
import { buildCapabilityStatus, mergeVersionHints } from "./capabilities.js";
import { createContextHostFacade } from "./context.js";
import { debugDebug } from "../debug-logging.js";
const REGEX_API_NAMES = ["getTavernRegexes", "isCharacterTavernRegexesEnabled"];
@@ -36,7 +37,7 @@ function resolveProviderCandidate(candidate, options = {}) {
const resolved = candidate(options);
return isObjectLike(resolved) ? resolved : null;
} catch (error) {
console.debug("[ST-BME] host-adapter/regex provider 解析失败", error);
debugDebug("[ST-BME] host-adapter/regex provider 解析失败", error);
return null;
}
}

View File

@@ -1,5 +1,6 @@
import { buildCapabilityStatus, mergeVersionHints } from "./capabilities.js";
import { createContextHostFacade } from "./context.js";
import { debugDebug } from "../debug-logging.js";
const WORLDBOOK_API_NAMES = [
"getWorldbook",
@@ -40,7 +41,7 @@ function resolveProviderCandidate(candidate, options = {}) {
const resolved = candidate(options);
return isObjectLike(resolved) ? resolved : null;
} catch (error) {
console.debug("[ST-BME] host-adapter/worldbook provider 解析失败", error);
debugDebug("[ST-BME] host-adapter/worldbook provider 解析失败", error);
return null;
}
}

View File

@@ -72,6 +72,10 @@ import {
onRerollController,
runExtractionController,
} from "./extraction-controller.js";
import {
debugDebug,
debugLog,
} from "./debug-logging.js";
import {
extractMemories,
generateReflection,
@@ -371,6 +375,7 @@ function readRuntimeDebugSnapshot() {
const defaultSettings = {
enabled: true,
debugLoggingEnabled: false,
timeoutMs: 300000,
hideOldMessagesEnabled: false,
hideOldMessagesKeepLastN: 12,
@@ -1486,6 +1491,7 @@ function getMessageRecallRecord(messageIndex) {
}
function debugWithThrottle(cache, key, ...args) {
if (!globalThis.__stBmeDebugLoggingEnabled) return;
const now = Date.now();
const lastAt = cache.get(key) || 0;
if (now - lastAt < PERSISTED_RECALL_UI_DIAGNOSTIC_THROTTLE_MS) return;
@@ -2952,6 +2958,9 @@ function getSettings() {
mergedSettings.taskProfilesVersion = migrated.taskProfilesVersion;
mergedSettings.taskProfiles = migrated.taskProfiles;
extension_settings[MODULE_NAME] = mergedSettings;
globalThis.__stBmeDebugLoggingEnabled = Boolean(
mergedSettings.debugLoggingEnabled,
);
return mergedSettings;
}
@@ -2989,7 +2998,7 @@ async function applyMessageHideNow(reason = "manual-apply") {
getMessageHideSettings(),
getHideRuntimeAdapters(),
);
console.log("[ST-BME] 已应用旧楼层隐藏:", reason, result);
debugLog("[ST-BME] 已应用旧楼层隐藏:", reason, result);
return result;
} catch (error) {
console.warn("[ST-BME] 应用旧楼层隐藏失败:", reason, error);
@@ -3019,7 +3028,7 @@ async function runIncrementalMessageHide(reason = "incremental") {
getHideRuntimeAdapters(),
);
if (result?.active) {
console.log("[ST-BME] 已增量更新旧楼层隐藏:", reason, result);
debugLog("[ST-BME] 已增量更新旧楼层隐藏:", reason, result);
}
return result;
} catch (error) {
@@ -3034,7 +3043,7 @@ async function runIncrementalMessageHide(reason = "incremental") {
function clearMessageHideState(reason = "reset") {
try {
resetHideState(getHideRuntimeAdapters());
console.log("[ST-BME] 已重置旧楼层隐藏状态:", reason);
debugLog("[ST-BME] 已重置旧楼层隐藏状态:", reason);
} catch (error) {
console.warn("[ST-BME] 重置旧楼层隐藏状态失败:", reason, error);
}
@@ -3043,7 +3052,7 @@ function clearMessageHideState(reason = "reset") {
async function clearAllHiddenMessages(reason = "manual-clear") {
try {
const result = await unhideAll(getHideRuntimeAdapters());
console.log("[ST-BME] 已取消全部旧楼层隐藏:", reason, result);
debugLog("[ST-BME] 已取消全部旧楼层隐藏:", reason, result);
return result;
} catch (error) {
console.warn("[ST-BME] 取消全部旧楼层隐藏失败:", reason, error);
@@ -3485,7 +3494,7 @@ async function syncBmeChatManagerWithCurrentChat(
if (!chatId) {
await manager.closeCurrent();
console.debug("[ST-BME] IndexedDB 会话已关闭(无活动聊天)", {
debugDebug("[ST-BME] IndexedDB 会话已关闭(无活动聊天)", {
source,
});
return {
@@ -3496,7 +3505,7 @@ async function syncBmeChatManagerWithCurrentChat(
}
const db = await manager.switchChat(chatId);
console.debug("[ST-BME] IndexedDB 会话已同步", {
debugDebug("[ST-BME] IndexedDB 会话已同步", {
source,
chatId,
});
@@ -3715,7 +3724,7 @@ async function maybeMigrateLegacyGraphToIndexedDb(
const postMigrationSnapshot = await targetDb.exportSnapshot();
cacheIndexedDbSnapshot(normalizedChatId, postMigrationSnapshot);
console.debug("[ST-BME] legacy chat_metadata 图谱迁移完成", {
debugDebug("[ST-BME] legacy chat_metadata 图谱迁移完成", {
source,
chatId: normalizedChatId,
revision:
@@ -3990,7 +3999,7 @@ function applyIndexedDbSnapshotToRuntime(
removeGraphShadowSnapshot(normalizedChatId);
refreshPanelLiveState();
schedulePersistedRecallMessageUiRefresh(30);
console.debug("[ST-BME] 已从 IndexedDB 加载图谱", {
debugDebug("[ST-BME] 已从 IndexedDB 加载图谱", {
chatId: normalizedChatId,
source,
revision,
@@ -4766,7 +4775,7 @@ function scheduleGraphLoadRetry(
clearPendingGraphLoadRetry({ resetChatId: false });
pendingGraphLoadRetryChatId =
normalizedChatId || (allowPendingChat ? GRAPH_LOAD_PENDING_CHAT_ID : "");
console.debug(
debugDebug(
`[ST-BME] 图谱元数据尚未就绪,${delayMs}ms 后重试加载chat=${normalizedChatId || "pending"}attempt=${attemptIndex + 1}reason=${reason}`,
);
@@ -5543,7 +5552,7 @@ async function ensureVectorReadyIfNeeded(
currentGraph.vectorIndexState.lastWarning = "";
saveGraphToChat({ reason: "vector-auto-repair-succeeded" });
console.log("[ST-BME] 向量状态已自动修复:", reason, result.stats);
debugLog("[ST-BME] 向量状态已自动修复:", reason, result.stats);
return result;
}
@@ -5610,6 +5619,9 @@ async function loadServerSettings() {
const loaded = await response.json();
if (loaded && typeof loaded === "object" && !Array.isArray(loaded)) {
extension_settings[MODULE_NAME] = mergePersistedSettings(loaded);
globalThis.__stBmeDebugLoggingEnabled = Boolean(
extension_settings[MODULE_NAME]?.debugLoggingEnabled,
);
saveSettingsDebounced();
}
} catch (error) {
@@ -5670,6 +5682,9 @@ function updateModuleSettings(patch = {}) {
const settings = getSettings();
Object.assign(settings, patch);
extension_settings[MODULE_NAME] = settings;
globalThis.__stBmeDebugLoggingEnabled = Boolean(
settings.debugLoggingEnabled,
);
saveSettingsDebounced();
if (
@@ -9834,11 +9849,11 @@ async function onReembedDirect() {
getExtensionPath: () => `scripts/extensions/third-party/${MODULE_NAME}`,
isTrivialUserInput,
preparePlannerRecallHandoff,
runPlannerRecallForEna,
runPlannerRecallForEna,
});
console.log("[ST-BME] Ena Planner module loaded");
debugLog("[ST-BME] Ena Planner module loaded");
} catch (error) {
console.warn("[ST-BME] Ena Planner module load failed:", error);
}
console.log("[ST-BME] 初始化完成");
debugLog("[ST-BME] 初始化完成");
})();

11
llm.js
View File

@@ -4,6 +4,7 @@
import { getRequestHeaders } from "../../../../script.js";
import { extension_settings } from "../../../extensions.js";
import { chat_completion_sources, sendOpenAIRequest } from "../../../openai.js";
import { debugLog, debugWarn } from "./debug-logging.js";
import { resolveTaskGenerationOptions } from "./generation-options.js";
import { resolveConfiguredTimeoutMs } from "./request-timeout.js";
import { applyTaskRegex } from "./task-regex.js";
@@ -1585,13 +1586,13 @@ export async function callLLMForJSON({
);
{
const asmUser = assembledMessages.filter((m) => m?.role === "user");
console.log(
debugLog(
`[ST-BME][prompt-diag] buildJsonAttemptMessages: ` +
`total=${assembledMessages.length}, user=${asmUser.length}, ` +
`roles=[${assembledMessages.map((m) => m?.role).join(",")}]`,
);
for (const m of asmUser) {
console.log(
debugLog(
`[ST-BME][prompt-diag] assembled user: len=${String(m.content || "").length}, ` +
`preview="${String(m.content || "").slice(0, 80)}..."`,
);
@@ -1605,18 +1606,18 @@ export async function callLLMForJSON({
const rcMsgs = Array.isArray(requestCleaning.messages) ? requestCleaning.messages : [];
const rcUser = rcMsgs.filter((m) => m?.role === "user");
const dbg = requestCleaning.debug || {};
console.log(
debugLog(
`[ST-BME][prompt-diag] applyTaskFinalInputRegex: ` +
`total=${rcMsgs.length}, user=${rcUser.length}, ` +
`changed=${dbg.changed}, applied=${dbg.applied}, ` +
`roles=[${rcMsgs.map((m) => m?.role).join(",")}]`,
);
if (rcUser.length === 0 && assembledMessages.filter((m) => m?.role === "user").length > 0) {
console.warn(
debugWarn(
`[ST-BME][prompt-diag] *** USER MESSAGES LOST during applyTaskFinalInputRegex! ***`,
);
for (const rule of dbg.appliedRules || []) {
console.warn(`[ST-BME][prompt-diag] applied rule: ${JSON.stringify(rule)}`);
debugWarn(`[ST-BME][prompt-diag] applied rule: ${JSON.stringify(rule)}`);
}
}
}

View File

@@ -1,3 +1,5 @@
import { debugLog } from "./debug-logging.js";
function resolvePanelTheme(settings) {
return settings?.panelTheme || "crimson";
}
@@ -83,7 +85,7 @@ export async function initializePanelBridgeController(runtime) {
});
injectOptionsMenuEntry(runtime);
runtime.console.log("[ST-BME] 操控面板初始化完成");
debugLog("[ST-BME] 操控面板初始化完成");
} catch (panelError) {
runtime.console.error(
"[ST-BME] 操控面板加载失败(核心功能不受影响):",

View File

@@ -1079,6 +1079,32 @@
</div>
</div>
<div class="bme-config-card">
<div class="bme-config-card-head">
<div>
<div class="bme-config-card-title">调试日志</div>
<div class="bme-config-card-subtitle">
默认关闭,需要排查问题时再临时打开浏览器控制台日志。
</div>
</div>
</div>
<label
class="bme-toggle-item"
for="bme-setting-debug-logging-enabled"
>
<span class="bme-toggle-copy">
<span class="bme-toggle-title">启用调试日志</span>
<span class="bme-toggle-desc">
只在开启后输出内部诊断日志与流程跟踪,可能包含脱敏后的 prompt 组装摘要。
</span>
</span>
<input
id="bme-setting-debug-logging-enabled"
type="checkbox"
/>
</label>
</div>
<div class="bme-config-card">
<div class="bme-config-card-head">
<div>

View File

@@ -1580,6 +1580,10 @@ function _refreshConfigTab() {
_refreshPlannerLauncher();
_setCheckboxValue("bme-setting-enabled", settings.enabled ?? true);
_setCheckboxValue(
"bme-setting-debug-logging-enabled",
settings.debugLoggingEnabled ?? false,
);
_setCheckboxValue(
"bme-setting-hide-old-messages-enabled",
settings.hideOldMessagesEnabled ?? false,
@@ -1920,6 +1924,9 @@ function _bindConfigControls() {
_patchSettings({ enabled: checked });
_refreshGuardedConfigStates();
});
bindCheckbox("bme-setting-debug-logging-enabled", (checked) => {
_patchSettings({ debugLoggingEnabled: checked });
});
bindCheckbox("bme-setting-hide-old-messages-enabled", (checked) => {
_patchSettings({ hideOldMessagesEnabled: checked });
});

View File

@@ -1,6 +1,7 @@
// ST-BME: Prompt Builder
// 统一负责任务预设块排序、变量渲染,以及世界书/EJS 上下文接入。
import { debugLog, debugWarn } from "./debug-logging.js";
import { getActiveTaskProfile, getLegacyPromptForTask } from "./prompt-profiles.js";
import { sanitizeMvuContent } from "./mvu-compat.js";
import { resolveTaskWorldInfo } from "./task-worldinfo.js";
@@ -1098,7 +1099,7 @@ export async function buildTaskPrompt(settings = {}, taskType, context = {}) {
let assistantRoleBlockCount = 0;
let systemRoleBlockCount = 0;
console.log(
debugLog(
`[ST-BME][prompt-diag] buildTaskPrompt: taskType=${taskType}, ` +
`total blocks=${blocks.length}, ` +
`block roles=[${blocks.map((b) => `${b.name}(${b.role},${b.enabled !== false ? "on" : "off"})`).join(", ")}]`,
@@ -1125,7 +1126,7 @@ export async function buildTaskPrompt(settings = {}, taskType, context = {}) {
}
if (role === "user") {
console.log(
debugLog(
`[ST-BME][prompt-diag] user block "${block.name || block.id}": ` +
`type=${block.type}, contentLen=${String(content || "").length}, ` +
`rawContentLen=${String(block.content || "").length}, ` +
@@ -1154,7 +1155,7 @@ export async function buildTaskPrompt(settings = {}, taskType, context = {}) {
if (!String(content || "").trim()) {
if (role === "user" && String(block.content || "").trim()) {
console.warn(
debugWarn(
`[ST-BME] buildTaskPrompt: user block "${block.name || block.id}" ` +
`content emptied during sanitization! ` +
`original length=${String(block.content || "").length}, ` +
@@ -1220,7 +1221,7 @@ export async function buildTaskPrompt(settings = {}, taskType, context = {}) {
}
}
console.log(
debugLog(
`[ST-BME][prompt-diag] buildTaskPrompt done: ` +
`executionMessages=${executionMessages.length}, ` +
`userBlocks=${userRoleBlockCount}, systemBlocks=${systemRoleBlockCount}, ` +
@@ -1405,7 +1406,7 @@ export function buildTaskLlmPayload(promptBuild = null, fallbackUserPrompt = "")
const userBlocksAfterSanitize = executionMessages.filter(
(m) => m?.role === "user",
);
console.warn(
debugWarn(
`[ST-BME] buildTaskLlmPayload fallback triggered: ` +
`user blocks in promptBuild=${userBlocksBefore.length}, ` +
`after recreate=${userBlocksAfterRaw.length}, ` +
@@ -1415,7 +1416,7 @@ export function buildTaskLlmPayload(promptBuild = null, fallbackUserPrompt = "")
);
if (userBlocksBefore.length > 0) {
for (const block of userBlocksBefore) {
console.warn(
debugWarn(
`[ST-BME] user block "${block.blockName || block.blockId}": ` +
`content length=${String(block.content || "").length}, ` +
`content preview="${String(block.content || "").slice(0, 80)}..."`,
@@ -1423,7 +1424,7 @@ export function buildTaskLlmPayload(promptBuild = null, fallbackUserPrompt = "")
}
}
if (blockedContents.length > 0) {
console.warn(
debugWarn(
`[ST-BME] blockedContents lengths: [${blockedContents.map((c) => String(c || "").length).join(", ")}]`,
);
}

View File

@@ -1,5 +1,7 @@
// ST-BME: 召回输入解析与注入控制器(纯函数)
import { debugLog, debugWarn } from "./debug-logging.js";
export function buildRecallRecentMessagesController(
chat,
limit,
@@ -174,7 +176,7 @@ export function applyRecallInjectionController(
if (injectionText) {
const tokens = runtime.estimateTokens(injectionText);
runtime.console.log(
debugLog(
`[ST-BME] 注入 ${tokens} 估算 tokens, Core=${result.stats.coreCount}, Recall=${result.stats.recallCount}`,
);
runtime.persistRecallInjectionRecord?.({
@@ -287,12 +289,12 @@ export function applyRecallInjectionController(
}
export async function runRecallController(runtime, options = {}) {
console.warn("[ST-BME:DIAG:RECALL] runRecallController entered");
debugWarn("[ST-BME:DIAG:RECALL] runRecallController entered");
if (runtime.getIsRecalling()) {
runtime.abortRecallStageWithReason("旧召回已取消,正在启动新的召回");
const settle = await runtime.waitForActiveRecallToSettle();
if (!settle.settled && runtime.getIsRecalling()) {
console.warn("[ST-BME:DIAG:RECALL] EXIT: 上一轮召回仍在清理");
debugWarn("[ST-BME:DIAG:RECALL] EXIT: 上一轮召回仍在清理");
runtime.setLastRecallStatus(
"召回忙",
"上一轮召回仍在清理,请稍后重试",
@@ -308,18 +310,18 @@ export async function runRecallController(runtime, options = {}) {
}
const hasGraph = !!runtime.getCurrentGraph();
console.warn("[ST-BME:DIAG:RECALL] hasGraph:", hasGraph);
debugWarn("[ST-BME:DIAG:RECALL] hasGraph:", hasGraph);
if (!hasGraph) {
console.warn("[ST-BME:DIAG:RECALL] EXIT: 当前无图谱");
debugWarn("[ST-BME:DIAG:RECALL] EXIT: 当前无图谱");
return runtime.createRecallRunResult("skipped", {
reason: "当前无图谱",
});
}
const settings = runtime.getSettings();
console.warn("[ST-BME:DIAG:RECALL] settings.enabled:", settings.enabled, "recallEnabled:", settings.recallEnabled);
debugWarn("[ST-BME:DIAG:RECALL] settings.enabled:", settings.enabled, "recallEnabled:", settings.recallEnabled);
if (!settings.enabled || !settings.recallEnabled) {
console.warn("[ST-BME:DIAG:RECALL] EXIT: 召回功能未启用");
debugWarn("[ST-BME:DIAG:RECALL] EXIT: 召回功能未启用");
return runtime.createRecallRunResult("skipped", {
reason: "召回功能未启用",
});
@@ -330,10 +332,10 @@ export async function runRecallController(runtime, options = {}) {
: runtime.isGraphReadable();
const chatId = typeof runtime.getCurrentChatId === "function" ? runtime.getCurrentChatId() : "(no fn)";
const loadState = runtime.getGraphPersistenceLoadState?.() || "(no fn)";
console.warn("[ST-BME:DIAG:RECALL] isReadableForRecall:", isReadableForRecall, "chatId:", chatId, "loadState:", loadState);
debugWarn("[ST-BME:DIAG:RECALL] isReadableForRecall:", isReadableForRecall, "chatId:", chatId, "loadState:", loadState);
if (!isReadableForRecall) {
const reason = runtime.getGraphMutationBlockReason("召回");
console.warn("[ST-BME:DIAG:RECALL] EXIT: 图谱不可读 -", reason);
debugWarn("[ST-BME:DIAG:RECALL] EXIT: 图谱不可读 -", reason);
runtime.setLastRecallStatus("等待图谱加载", reason, "warning", {
syncRuntime: true,
});
@@ -407,7 +409,7 @@ export async function runRecallController(runtime, options = {}) {
recallInput.deliveryMode =
String(options.deliveryMode || "immediate").trim() || "immediate";
runtime.console.log("[ST-BME] 开始召回", {
debugLog("[ST-BME] 开始召回", {
source: recallInput.source,
sourceLabel: recallInput.sourceLabel,
hookName: recallInput.hookName,

View File

@@ -2,6 +2,7 @@
// 融合向量预筛PeroCore+ 图扩散PeroCore PEDSA+ 可选 LLM 精确召回
// v2: + 认知边界过滤(RoleRAG) + 双记忆交叉检索(AriGraph) + 概率触发
import { debugLog } from "./debug-logging.js";
import { diffuseAndRank } from "./diffusion.js";
import { hybridScore, reinforceAccessBatch } from "./dynamics.js";
import {
@@ -838,7 +839,7 @@ export async function retrieve({
maxSegments: multiIntentMaxSegments,
},
);
console.log(
debugLog(
`[ST-BME] 检索开始: ${nodeCount} 个活跃节点${enableVisibility ? " (认知边界已启用)" : ""}`,
);
@@ -877,7 +878,7 @@ export async function retrieve({
const vectorStartedAt = nowMs();
if (enableVectorPrefilter && vectorValidation.valid) {
console.log("[ST-BME] 第1层: 向量预筛");
debugLog("[ST-BME] 第1层: 向量预筛");
const queryPlan = buildVectorQueryPlan(contextQueryBlend, {
enableMultiIntent,
maxSegments: multiIntentMaxSegments,
@@ -958,7 +959,7 @@ export async function retrieve({
const diffusionStartedAt = nowMs();
if (enableGraphDiffusion) {
console.log("[ST-BME] 第2层: PEDSA 图扩散");
debugLog("[ST-BME] 第2层: PEDSA 图扩散");
const seeds = [
...vectorResults.map((v) => ({ id: v.nodeId, energy: v.score })),
...exactEntityAnchors.map((item) => ({ id: item.nodeId, energy: 2.0 })),
@@ -1016,7 +1017,7 @@ export async function retrieve({
retrievalMeta.diffusionHits = diffusionResults.length;
retrievalMeta.timings.diffusion = roundMs(nowMs() - diffusionStartedAt);
console.log("[ST-BME] 第3层: 混合评分");
debugLog("[ST-BME] 第3层: 混合评分");
const scoreMap = new Map();
@@ -1168,7 +1169,7 @@ export async function retrieve({
let llmDurationMs = 0;
if (enableLLMRecall && nodeCount > 0) {
console.log("[ST-BME] LLM 精确召回");
debugLog("[ST-BME] LLM 精确召回");
llmCandidates = resolveCandidatePool(
scoredNodes,
normalizedLlmCandidatePool,
@@ -1247,7 +1248,7 @@ export async function retrieve({
reinforceAccessBatch(selectedNodes);
console.log(`[ST-BME] 检索完成: 选中 ${selectedNodeIds.length} 个节点`);
debugLog(`[ST-BME] 检索完成: 选中 ${selectedNodeIds.length} 个节点`);
if (enableProbRecall && probRecallChance > 0) {
const selectedSet = new Set(selectedNodeIds);
@@ -1265,7 +1266,7 @@ export async function retrieve({
for (const c of candidates) {
if (Math.random() < probability) {
selectedNodeIds.push(c.id);
console.log(
debugLog(
`[ST-BME] 概率触发: ${c.fields?.name || c.fields?.summary || c.id}`,
);
}

View File

@@ -3,6 +3,7 @@
// 同时叠加任务本地规则,并按任务阶段执行。
import { extension_settings, getContext } from "../../../extensions.js";
import { debugDebug } from "./debug-logging.js";
import { getHostAdapter } from "./host-adapter/index.js";
import {
getActiveTaskProfile,
@@ -236,7 +237,7 @@ function getRegexHost() {
};
}
} catch (error) {
console.debug(
debugDebug(
"[ST-BME] task-regex 读取 regex bridge 失败,回退到 legacy 宿主接口",
error,
);

View File

@@ -13,6 +13,7 @@ import {
isMvuTaggedWorldInfoNameOrComment,
sanitizeMvuContent,
} from "./mvu-compat.js";
import { debugDebug } from "./debug-logging.js";
const WI_POSITION = {
before: 0,
@@ -249,7 +250,7 @@ async function getWorldbookHost() {
};
}
} catch (error) {
console.debug(
debugDebug(
"[ST-BME] task-worldinfo 读取 worldbook bridge 失败,回退到 legacy 宿主接口",
error,
);
@@ -759,7 +760,7 @@ async function loadNormalizedWorldbookEntries(
]),
);
} catch (error) {
console.debug(
debugDebug(
`[ST-BME] task-worldinfo 读取 lorebook comment 失败: ${normalizedName}`,
error,
);
@@ -827,7 +828,7 @@ async function collectAllWorldbookEntries(worldbookHost = null) {
capabilityStatus?.supplementedCapabilities || [];
const missingCapabilities = capabilityStatus?.missingCapabilities || [];
if (supplementedCapabilities.length > 0) {
console.debug(
debugDebug(
`[ST-BME] task-worldinfo worldbook bridge 已通过 legacy 补齐关键能力: ${supplementedCapabilities.join(", ")} [${sourceTag}]`,
);
}
@@ -849,7 +850,7 @@ async function collectAllWorldbookEntries(worldbookHost = null) {
? resolved.additional.map((name) => normalizeKey(name)).filter(Boolean)
: [];
} catch (error) {
console.debug(
debugDebug(
`[ST-BME] task-worldinfo 读取角色世界书失败 [${sourceTag}]`,
error,
);
@@ -928,7 +929,7 @@ async function collectAllWorldbookEntries(worldbookHost = null) {
);
allEntries.push(...entries);
} catch (error) {
console.debug(
debugDebug(
`[ST-BME] task-worldinfo 读取世界书失败: ${normalizedName} [${sourceTag}]`,
error,
);

View File

@@ -40,10 +40,10 @@ async function loadSettingsCompatHelpers() {
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]*?^}\n/m,
/function migrateLegacyAutoMaintenanceSettings\(loaded = \{\}\) \{[\s\S]*?^}\r?\n/m,
);
const mergeMatch = source.match(
/function mergePersistedSettings\(loaded = \{\}\) \{[\s\S]*?^}\n/m,
/function mergePersistedSettings\(loaded = \{\}\) \{[\s\S]*?^}\r?\n/m,
);
if (!settingsMatch || !compatMatch || !mergeMatch) {
@@ -124,6 +124,7 @@ assert.equal(defaultSettings.injectUserPovMemory, true);
assert.equal(defaultSettings.injectObjectiveGlobalMemory, true);
assert.equal(defaultSettings.injectDepth, 9999);
assert.equal(defaultSettings.enabled, true);
assert.equal(defaultSettings.debugLoggingEnabled, false);
assert.equal(defaultSettings.enableReflection, true);
assert.equal(defaultSettings.consolidationAutoMinNewNodes, 2);
assert.equal(defaultSettings.enableAutoCompression, true);