Integrate ENA planner task profiles

This commit is contained in:
Youzini-afk
2026-04-23 16:25:05 +08:00
parent dfe0ba3d6e
commit efff3b15b3
6 changed files with 759 additions and 230 deletions

View File

@@ -117,6 +117,30 @@ function getSharedLlmPresetState() {
return sanitizeLlmPresetSettings(settings || {});
}
function openPlannerTaskPresetWorkspace() {
const taskTabBtn = document.querySelector('.bme-tab-btn[data-tab="task"]');
taskTabBtn?.click();
const activatePlannerTaskType = () => {
const plannerBtn = document.querySelector(
'[data-task-action="switch-task-type"][data-task-type="planner"]',
);
plannerBtn?.click();
return Boolean(plannerBtn);
};
if (activatePlannerTaskType()) {
return true;
}
requestAnimationFrame(() => {
requestAnimationFrame(() => {
activatePlannerTaskType();
});
});
return Boolean(taskTabBtn);
}
function buildPlannerLlmSnapshot(source = {}) {
return {
llmApiUrl: String(source?.llmApiUrl || '').trim(),
@@ -503,13 +527,6 @@ function collectPatch() {
customPrefix: getVal('bme-planner-prefix-custom').trim(),
apiKey: getVal('bme-planner-api-key'),
model: getVal('bme-planner-model').trim(),
stream: toBool(getVal('bme-planner-stream'), false),
temperature: toNum(getVal('bme-planner-temp'), 1),
top_p: toNum(getVal('bme-planner-top-p'), 1),
top_k: Math.floor(toNum(getVal('bme-planner-top-k'), 0)),
presence_penalty: getVal('bme-planner-pp').trim(),
frequency_penalty: getVal('bme-planner-fp').trim(),
max_tokens: getVal('bme-planner-mt').trim(),
},
includeGlobalWorldbooks: toBool(getVal('bme-planner-include-global-wb'), false),
excludeWorldbookPosition4: toBool(getVal('bme-planner-wb-pos4'), true),
@@ -519,9 +536,6 @@ function collectPatch() {
chatExcludeTags: csvToArr(getVal('bme-planner-exclude-tags')),
logsPersist: toBool(getVal('bme-planner-logs-persist'), true),
logsMax: Math.max(1, Math.min(200, Math.floor(toNum(getVal('bme-planner-logs-max'), 20)))),
promptBlocks: cfgCache?.promptBlocks || [],
promptTemplates: cfgCache?.promptTemplates || {},
activePromptTemplate: $('bme-planner-tpl-select')?.value || '',
};
}
@@ -682,6 +696,15 @@ function bindOnce(section) {
scheduleSave();
});
$('bme-planner-open-task-presets')?.addEventListener('click', () => {
const opened = openPlannerTaskPresetWorkspace();
if (!opened) {
setLocalStatus('bme-planner-api-status', '未找到任务预设工作区,请手动切到“任务 -> 规划”', 'error');
return;
}
setLocalStatus('bme-planner-api-status', '已切换到“任务 -> 规划”预设编辑器', 'success');
});
/* Prompts + templates */
$('bme-planner-keep-tags')?.addEventListener('change', onKeepTagsBlur);

View File

@@ -3031,170 +3031,23 @@
<div class="bme-config-card">
<div class="bme-config-card-head">
<div>
<div class="bme-config-card-title">规划 LLM · 生成参数</div>
<div class="bme-config-card-title">生成参数 · Prompt 编排</div>
<div class="bme-config-card-subtitle">
流式输出用于实时预览,数值留空表示不覆盖渠道默认
这两部分现在统一收敛到任务预设里的“规划”任务,避免 ENA 面板和任务面板各维护一套配置
</div>
</div>
</div>
<div class="bme-config-row">
<label for="bme-planner-stream">流式输出</label>
<select id="bme-planner-stream" class="bme-config-input">
<option value="true">开启</option>
<option value="false">关闭</option>
</select>
</div>
<div class="bme-planner-param-grid">
<div class="bme-config-row">
<label for="bme-planner-temp">Temperature</label>
<input
id="bme-planner-temp"
class="bme-config-input"
type="number"
step="0.1"
min="0"
max="2"
/>
</div>
<div class="bme-config-row">
<label for="bme-planner-top-p">Top P</label>
<input
id="bme-planner-top-p"
class="bme-config-input"
type="number"
step="0.05"
min="0"
max="1"
/>
</div>
<div class="bme-config-row">
<label for="bme-planner-top-k">Top K</label>
<input
id="bme-planner-top-k"
class="bme-config-input"
type="number"
step="1"
min="0"
/>
</div>
<div class="bme-config-row">
<label for="bme-planner-pp">Presence penalty</label>
<input
id="bme-planner-pp"
class="bme-config-input"
type="text"
placeholder="-2 ~ 2"
/>
</div>
<div class="bme-config-row">
<label for="bme-planner-fp">Frequency penalty</label>
<input
id="bme-planner-fp"
class="bme-config-input"
type="text"
placeholder="-2 ~ 2"
/>
</div>
<div class="bme-config-row">
<label for="bme-planner-mt">最大 Token 数</label>
<input
id="bme-planner-mt"
class="bme-config-input"
type="text"
placeholder="留空则不限制"
/>
</div>
</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>
<div class="bme-config-row">
<label for="bme-planner-tpl-select">活动模板</label>
<select id="bme-planner-tpl-select" class="bme-config-input">
<option value="">-- 选择模板 --</option>
</select>
<div class="bme-config-help">
现在每个规划预设都可以同时携带自己的生成参数和 Prompt block。ENA 这里仍然只负责连接配置、上下文来源、输出过滤和调试日志。
</div>
<div class="bme-config-actions">
<button
class="bme-config-secondary-btn"
id="bme-planner-tpl-save"
id="bme-planner-open-task-presets"
type="button"
>
<i class="fa-solid fa-floppy-disk"></i>
<span>覆盖保存</span>
</button>
<button
class="bme-config-secondary-btn"
id="bme-planner-tpl-saveas"
type="button"
>
<i class="fa-solid fa-file-circle-plus"></i>
<span>另存为</span>
</button>
<button
class="bme-config-secondary-btn bme-config-danger-btn"
id="bme-planner-tpl-delete"
type="button"
>
<i class="fa-solid fa-trash-can"></i>
<span>删除</span>
</button>
</div>
<div class="bme-planner-undo-bar" id="bme-planner-tpl-undo" hidden>
<span
>模板 <strong id="bme-planner-tpl-undo-name"></strong> 已删除</span
>
<button
class="bme-config-secondary-btn"
id="bme-planner-tpl-undo-btn"
type="button"
>
撤销
</button>
</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">
每个块会作为一条独立消息发送给规划 LLM。系统会在块之后自动追加角色卡、世界书、BME 结构化记忆、聊天历史和历史 plot。
</div>
</div>
</div>
<div id="bme-planner-prompt-list" class="bme-planner-prompt-list"></div>
<div
class="bme-planner-prompt-empty"
id="bme-planner-prompt-empty"
hidden
>
暂无提示词块
</div>
<div class="bme-config-actions">
<button
class="bme-config-secondary-btn"
id="bme-planner-add-prompt"
type="button"
>
<i class="fa-solid fa-plus"></i>
<span>添加块</span>
</button>
<button
class="bme-config-secondary-btn bme-config-danger-btn"
id="bme-planner-reset-prompt"
type="button"
>
<i class="fa-solid fa-rotate-left"></i>
<span>恢复默认</span>
<i class="fa-solid fa-list-check"></i>
<span>打开任务预设 · 规划</span>
</button>
</div>
</div>

View File

@@ -173,6 +173,7 @@ const GRAPH_WRITE_ACTION_IDS = [
const TASK_PROFILE_GENERATION_GROUPS = [
{
title: "API 配置",
excludeTaskTypes: ["planner"],
fields: [
{
key: "llm_preset",
@@ -8412,6 +8413,8 @@ function _getTaskProfileWorkspaceState(settings = _getSettings?.() || {}) {
currentGlobalRegexRuleId = globalRegexRules[0]?.id || "";
}
const builtinBlockDefinitions = getBuiltinBlockDefinitions(currentTaskProfileTaskType);
return {
settings,
taskProfiles,
@@ -8431,7 +8434,7 @@ function _getTaskProfileWorkspaceState(settings = _getSettings?.() || {}) {
regexRules.find((rule) => rule.id === currentTaskProfileRuleId) || null,
selectedGlobalRegexRule:
globalRegexRules.find((rule) => rule.id === currentGlobalRegexRuleId) || null,
builtinBlockDefinitions: getBuiltinBlockDefinitions(),
builtinBlockDefinitions,
runtimeDebug,
};
}
@@ -9626,11 +9629,11 @@ async function _handleTaskProfileWorkspaceClick(event) {
document.getElementById("bme-task-profile-import-all")?.click();
return;
case "restore-all-profiles": {
const taskTypes = getTaskTypeOptions().map((t) => t.id);
const confirmed = window.confirm(
"这会将全部 6 个任务的默认预设恢复为出厂状态。已保存的自定义预设不受影响,通用正则规则也不受影响。是否继续?",
`这会将全部 ${taskTypes.length} 个任务的默认预设恢复为出厂状态。已保存的自定义预设不受影响,通用正则规则也不受影响。是否继续?`,
);
if (!confirmed) return;
const taskTypes = getTaskTypeOptions().map((t) => t.id);
let restored = state.taskProfiles;
const extraPatch = {};
for (const tt of taskTypes) {
@@ -9727,6 +9730,7 @@ function _renderTaskProfileWorkspace(state) {
state.taskTypeOptions.find((item) => item.id === state.taskType) ||
state.taskTypeOptions[0];
const profileUpdatedAt = _formatTaskProfileTime(state.profile.updatedAt);
const totalTaskTypes = Array.isArray(state.taskTypeOptions) ? state.taskTypeOptions.length : 0;
return `
<div class="bme-task-shell">
@@ -9757,10 +9761,10 @@ function _renderTaskProfileWorkspace(state) {
</div>
</div>
<div class="bme-task-action-bar-right">
<button class="bme-config-secondary-btn bme-bulk-profile-btn bme-task-btn-danger" data-task-action="restore-all-profiles" type="button" title="恢复全部 6 个任务的默认预设">
<button class="bme-config-secondary-btn bme-bulk-profile-btn bme-task-btn-danger" data-task-action="restore-all-profiles" type="button" title="恢复全部 ${_escAttr(String(totalTaskTypes || 0))} 个任务的默认预设">
<i class="fa-solid fa-arrows-rotate"></i><span>恢复全部</span>
</button>
<button class="bme-config-secondary-btn bme-bulk-profile-btn" data-task-action="export-all-profiles" type="button" title="导出全部 6 个任务预设">
<button class="bme-config-secondary-btn bme-bulk-profile-btn" data-task-action="export-all-profiles" type="button" title="导出全部 ${_escAttr(String(totalTaskTypes || 0))} 个任务预设">
<i class="fa-solid fa-file-export"></i><span>导出全部</span>
</button>
<button class="bme-config-secondary-btn bme-bulk-profile-btn" data-task-action="import-all-profiles" type="button" title="导入全部预设(覆盖当前)">
@@ -9881,9 +9885,12 @@ function _renderTaskPromptTab(state) {
function _renderTaskGenerationTab(state) {
const inputGroups = TASK_PROFILE_INPUT_GROUPS[state.taskType] || [];
const generationGroups = TASK_PROFILE_GENERATION_GROUPS.filter(
(group) => !Array.isArray(group.excludeTaskTypes) || !group.excludeTaskTypes.includes(state.taskType),
);
return `
<div class="bme-task-tab-body">
${TASK_PROFILE_GENERATION_GROUPS.map(
${generationGroups.map(
(group) => `
<div class="bme-config-card">
<div class="bme-config-card-head">