fix: reduce startup load risks for plugin init

This commit is contained in:
Youzini-afk
2026-03-26 23:39:53 +08:00
parent 28616fc177
commit ae82d89605
4 changed files with 177 additions and 23 deletions

View File

@@ -60,11 +60,6 @@ import {
rollbackBatch,
snapshotProcessedMessageHashes,
} from "./runtime-state.js";
import {
getRuntimeDebugSnapshot as readRuntimeDebugSnapshot,
recordHostCapabilitySnapshot,
recordInjectionSnapshot,
} from "./runtime-debug.js";
import { DEFAULT_NODE_SCHEMA, validateSchema } from "./schema.js";
import {
deleteBackendVectorHashesForRecovery,
@@ -87,6 +82,75 @@ const GRAPH_METADATA_KEY = "st_bme_graph";
const SERVER_SETTINGS_FILENAME = "st-bme-settings.json";
const SERVER_SETTINGS_URL = `/user/files/${SERVER_SETTINGS_FILENAME}`;
function cloneRuntimeDebugValue(value, fallback = null) {
if (value == null) {
return fallback;
}
try {
return JSON.parse(JSON.stringify(value));
} catch {
return fallback ?? value;
}
}
function getRuntimeDebugState() {
const stateKey = "__stBmeRuntimeDebugState";
if (
!globalThis[stateKey] ||
typeof globalThis[stateKey] !== "object"
) {
globalThis[stateKey] = {
hostCapabilities: null,
taskPromptBuilds: {},
taskLlmRequests: {},
injections: {},
updatedAt: "",
};
}
return globalThis[stateKey];
}
function touchRuntimeDebugState() {
const state = getRuntimeDebugState();
state.updatedAt = new Date().toISOString();
return state;
}
function recordHostCapabilitySnapshot(snapshot = null) {
const state = touchRuntimeDebugState();
state.hostCapabilities = cloneRuntimeDebugValue(snapshot, null);
}
function recordInjectionSnapshot(kind, snapshot = {}) {
const normalizedKind = String(kind || "").trim() || "default";
const state = touchRuntimeDebugState();
state.injections[normalizedKind] = {
updatedAt: new Date().toISOString(),
...cloneRuntimeDebugValue(snapshot, {}),
};
}
function readRuntimeDebugSnapshot() {
const state = getRuntimeDebugState();
return cloneRuntimeDebugValue(
{
hostCapabilities: state.hostCapabilities,
taskPromptBuilds: state.taskPromptBuilds,
taskLlmRequests: state.taskLlmRequests,
injections: state.injections,
updatedAt: state.updatedAt,
},
{
hostCapabilities: null,
taskPromptBuilds: {},
taskLlmRequests: {},
injections: {},
updatedAt: "",
},
);
}
// ==================== 默认设置 ====================
const defaultSettings = {

40
llm.js
View File

@@ -5,7 +5,6 @@ import { getRequestHeaders } from "../../../../script.js";
import { extension_settings } from "../../../extensions.js";
import { chat_completion_sources, sendOpenAIRequest } from "../../../openai.js";
import { resolveTaskGenerationOptions } from "./generation-options.js";
import { recordTaskLlmRequest } from "./runtime-debug.js";
const MODULE_NAME = "st_bme";
const LLM_REQUEST_TIMEOUT_MS = 300000;
@@ -13,6 +12,45 @@ const DEFAULT_TEXT_COMPLETION_TOKENS = 64000;
const DEFAULT_JSON_COMPLETION_TOKENS = 64000;
const RETRY_JSON_COMPLETION_TOKENS = 3200;
function cloneRuntimeDebugValue(value, fallback = null) {
if (value == null) {
return fallback;
}
try {
return JSON.parse(JSON.stringify(value));
} catch {
return fallback ?? value;
}
}
function getRuntimeDebugState() {
const stateKey = "__stBmeRuntimeDebugState";
if (
!globalThis[stateKey] ||
typeof globalThis[stateKey] !== "object"
) {
globalThis[stateKey] = {
hostCapabilities: null,
taskPromptBuilds: {},
taskLlmRequests: {},
injections: {},
updatedAt: "",
};
}
return globalThis[stateKey];
}
function recordTaskLlmRequest(taskType, snapshot = {}) {
const normalizedTaskType = String(taskType || "").trim() || "unknown";
const state = getRuntimeDebugState();
state.taskLlmRequests[normalizedTaskType] = {
updatedAt: new Date().toISOString(),
...cloneRuntimeDebugValue(snapshot, {}),
};
state.updatedAt = new Date().toISOString();
}
function getLlmTestOverride(name) {
const override = globalThis.__stBmeTestOverrides?.llm?.[name];
return typeof override === "function" ? override : null;

View File

@@ -25,13 +25,27 @@ import {
getVectorIndexStats,
} from "./vector-index.js";
// 从 DEFAULT_TASK_BLOCKS 派生的合并文本(用于旧版兼容回退)
const DEFAULT_PROMPTS = Object.fromEntries(
Object.entries(DEFAULT_TASK_BLOCKS).map(([key, { role, format, rules }]) => [
key,
[role, format, rules].filter(Boolean).join("\n\n"),
]),
);
let defaultPromptCache = null;
function getDefaultPrompts() {
if (defaultPromptCache) {
return defaultPromptCache;
}
const prompts = {};
for (const [key, block] of Object.entries(DEFAULT_TASK_BLOCKS || {})) {
prompts[key] = [block?.role, block?.format, block?.rules]
.filter(Boolean)
.join("\n\n");
}
defaultPromptCache = prompts;
return prompts;
}
function getDefaultPromptText(taskType = "") {
return getDefaultPrompts()[taskType] || "";
}
const TASK_PROFILE_TABS = [
{ id: "generation", label: "生成参数" },
@@ -1028,27 +1042,27 @@ function _refreshConfigTab() {
_setInputValue(
"bme-setting-extract-prompt",
settings.extractPrompt || DEFAULT_PROMPTS.extract,
settings.extractPrompt || getDefaultPromptText("extract"),
);
_setInputValue(
"bme-setting-recall-prompt",
settings.recallPrompt || DEFAULT_PROMPTS.recall,
settings.recallPrompt || getDefaultPromptText("recall"),
);
_setInputValue(
"bme-setting-consolidation-prompt",
settings.consolidationPrompt || DEFAULT_PROMPTS.consolidation,
settings.consolidationPrompt || getDefaultPromptText("consolidation"),
);
_setInputValue(
"bme-setting-compress-prompt",
settings.compressPrompt || DEFAULT_PROMPTS.compress,
settings.compressPrompt || getDefaultPromptText("compress"),
);
_setInputValue(
"bme-setting-synopsis-prompt",
settings.synopsisPrompt || DEFAULT_PROMPTS.synopsis,
settings.synopsisPrompt || getDefaultPromptText("synopsis"),
);
_setInputValue(
"bme-setting-reflection-prompt",
settings.reflectionPrompt || DEFAULT_PROMPTS.reflection,
settings.reflectionPrompt || getDefaultPromptText("reflection"),
);
_refreshFetchedModelSelects(settings);
@@ -1262,7 +1276,7 @@ function _bindConfigControls() {
const targetId = button.dataset.targetId;
if (!settingKey || !promptKey || !targetId) return;
_patchSettings({ [settingKey]: "" }, { refreshPrompts: true });
_setInputValue(targetId, DEFAULT_PROMPTS[promptKey] || "");
_setInputValue(targetId, getDefaultPromptText(promptKey));
_refreshPromptCardStates();
});
button.dataset.bmeBound = "true";
@@ -1410,7 +1424,7 @@ function bindPromptText(id, settingKey, promptKey) {
element.addEventListener("change", update);
element.addEventListener("blur", () => {
if (!String(element.value || "").trim()) {
_setInputValue(id, DEFAULT_PROMPTS[promptKey] || "");
_setInputValue(id, getDefaultPromptText(promptKey));
}
});
element.dataset.bmeBound = "true";
@@ -2498,7 +2512,7 @@ function _renderTaskBlockEditor(state) {
const legacyField = getLegacyPromptFieldForTask(state.taskType);
const legacyValue =
legacyField && block.type === "legacyPrompt"
? state.settings?.[legacyField] || block.content || DEFAULT_PROMPTS[state.taskType] || ""
? state.settings?.[legacyField] || block.content || getDefaultPromptText(state.taskType) || ""
: block.content || "";
return `

View File

@@ -2,7 +2,6 @@
// 统一负责任务预设块排序、变量渲染,以及世界书/EJS 上下文接入。
import { getActiveTaskProfile, getLegacyPromptForTask } from "./prompt-profiles.js";
import { recordTaskPromptBuild } from "./runtime-debug.js";
import { resolveTaskWorldInfo } from "./task-worldinfo.js";
const WORLD_INFO_VARIABLE_KEYS = [
@@ -15,6 +14,45 @@ const WORLD_INFO_VARIABLE_KEYS = [
"taskAdditionalMessages",
];
function cloneRuntimeDebugValue(value, fallback = null) {
if (value == null) {
return fallback;
}
try {
return JSON.parse(JSON.stringify(value));
} catch {
return fallback ?? value;
}
}
function getRuntimeDebugState() {
const stateKey = "__stBmeRuntimeDebugState";
if (
!globalThis[stateKey] ||
typeof globalThis[stateKey] !== "object"
) {
globalThis[stateKey] = {
hostCapabilities: null,
taskPromptBuilds: {},
taskLlmRequests: {},
injections: {},
updatedAt: "",
};
}
return globalThis[stateKey];
}
function recordTaskPromptBuild(taskType, snapshot = {}) {
const normalizedTaskType = String(taskType || "").trim() || "unknown";
const state = getRuntimeDebugState();
state.taskPromptBuilds[normalizedTaskType] = {
updatedAt: new Date().toISOString(),
...cloneRuntimeDebugValue(snapshot, {}),
};
state.updatedAt = new Date().toISOString();
}
function getByPath(target, path) {
return String(path || "")
.split(".")