mirror of
https://github.com/Youzini-afk/ST-Bionic-Memory-Ecology.git
synced 2026-05-15 22:30:38 +08:00
feat: reuse shared llm presets for ena planner
This commit is contained in:
@@ -9,6 +9,12 @@
|
|||||||
* BME theming automatically.
|
* BME theming automatically.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
isSameLlmConfigSnapshot,
|
||||||
|
resolveDedicatedLlmProviderConfig,
|
||||||
|
sanitizeLlmPresetSettings,
|
||||||
|
} from '../llm/llm-preset-utils.js';
|
||||||
|
|
||||||
const SECTION_SELECTOR = '[data-config-section="planner"]';
|
const SECTION_SELECTOR = '[data-config-section="planner"]';
|
||||||
const AUTOSAVE_DELAY_MS = 600;
|
const AUTOSAVE_DELAY_MS = 600;
|
||||||
|
|
||||||
@@ -21,6 +27,7 @@ let fetchedModels = [];
|
|||||||
let undoState = null;
|
let undoState = null;
|
||||||
let fieldChangeHandler = null;
|
let fieldChangeHandler = null;
|
||||||
let autosaveInProgress = false;
|
let autosaveInProgress = false;
|
||||||
|
let externalGetSettings = null;
|
||||||
|
|
||||||
/* ── DOM helpers ────────────────────────────────────────────────────────── */
|
/* ── DOM helpers ────────────────────────────────────────────────────────── */
|
||||||
|
|
||||||
@@ -99,6 +106,118 @@ function genId() {
|
|||||||
catch { return `${Date.now()}_${Math.random().toString(36).slice(2, 8)}`; }
|
catch { return `${Date.now()}_${Math.random().toString(36).slice(2, 8)}`; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getSharedSettingsSnapshot() {
|
||||||
|
return typeof externalGetSettings === 'function'
|
||||||
|
? (externalGetSettings() || {})
|
||||||
|
: {};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSharedLlmPresetState() {
|
||||||
|
const settings = getSharedSettingsSnapshot();
|
||||||
|
return sanitizeLlmPresetSettings(settings || {});
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildPlannerLlmSnapshot(source = {}) {
|
||||||
|
return {
|
||||||
|
llmApiUrl: String(source?.llmApiUrl || '').trim(),
|
||||||
|
llmApiKey: String(source?.llmApiKey || '').trim(),
|
||||||
|
llmModel: String(source?.llmModel || '').trim(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCurrentPlannerLlmSnapshot() {
|
||||||
|
const rawUrl = String(
|
||||||
|
$('bme-planner-api-base')?.value ?? cfgCache?.api?.baseUrl ?? '',
|
||||||
|
).trim();
|
||||||
|
const resolved = resolveDedicatedLlmProviderConfig(rawUrl);
|
||||||
|
return buildPlannerLlmSnapshot({
|
||||||
|
llmApiUrl: resolved.apiUrl || rawUrl,
|
||||||
|
llmApiKey: $('bme-planner-api-key')?.value ?? cfgCache?.api?.apiKey ?? '',
|
||||||
|
llmModel: $('bme-planner-model')?.value ?? cfgCache?.api?.model ?? '',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizePlannerPresetSnapshot(preset = {}) {
|
||||||
|
const rawUrl = String(preset?.llmApiUrl || '').trim();
|
||||||
|
const resolved = resolveDedicatedLlmProviderConfig(rawUrl);
|
||||||
|
return buildPlannerLlmSnapshot({
|
||||||
|
llmApiUrl: resolved.apiUrl || rawUrl,
|
||||||
|
llmApiKey: preset?.llmApiKey || '',
|
||||||
|
llmModel: preset?.llmModel || '',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveMatchingPlannerLlmPresetName(snapshot = getCurrentPlannerLlmSnapshot()) {
|
||||||
|
const { presets, activePreset } = getSharedLlmPresetState();
|
||||||
|
const exactMatches = Object.keys(presets || {}).filter((name) =>
|
||||||
|
isSameLlmConfigSnapshot(snapshot, normalizePlannerPresetSnapshot(presets[name])),
|
||||||
|
);
|
||||||
|
if (exactMatches.length === 1) return exactMatches[0];
|
||||||
|
if (exactMatches.length > 1 && activePreset && exactMatches.includes(activePreset)) {
|
||||||
|
return activePreset;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function populatePlannerLlmPresetSelect(selectedPreset = resolveMatchingPlannerLlmPresetName()) {
|
||||||
|
const select = $('bme-planner-llm-preset-select');
|
||||||
|
if (!select) return;
|
||||||
|
|
||||||
|
while (select.options.length > 1) {
|
||||||
|
select.remove(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { presets } = getSharedLlmPresetState();
|
||||||
|
Object.keys(presets || {})
|
||||||
|
.sort((left, right) => left.localeCompare(right, 'zh-Hans-CN'))
|
||||||
|
.forEach((name) => {
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.value = name;
|
||||||
|
option.textContent = name;
|
||||||
|
select.appendChild(option);
|
||||||
|
});
|
||||||
|
|
||||||
|
select.value = selectedPreset || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncPlannerLlmPresetSelect() {
|
||||||
|
populatePlannerLlmPresetSelect(resolveMatchingPlannerLlmPresetName());
|
||||||
|
}
|
||||||
|
|
||||||
|
function inferPlannerApiConfigFromPreset(preset = {}) {
|
||||||
|
const rawUrl = String(preset?.llmApiUrl || '').trim();
|
||||||
|
const resolved = resolveDedicatedLlmProviderConfig(rawUrl);
|
||||||
|
let channel = 'openai';
|
||||||
|
if (resolved.providerId === 'google-ai-studio') channel = 'gemini';
|
||||||
|
else if (resolved.providerId === 'anthropic-claude') channel = 'claude';
|
||||||
|
|
||||||
|
return {
|
||||||
|
channel,
|
||||||
|
prefixMode: 'auto',
|
||||||
|
customPrefix: '',
|
||||||
|
baseUrl: resolved.apiUrl || rawUrl,
|
||||||
|
apiKey: String(preset?.llmApiKey || '').trim(),
|
||||||
|
model: String(preset?.llmModel || '').trim(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyPlannerLlmPresetToFields(name, preset = {}) {
|
||||||
|
const inferred = inferPlannerApiConfigFromPreset(preset);
|
||||||
|
const setVal = (id, value) => {
|
||||||
|
const el = $(id);
|
||||||
|
if (el) el.value = value;
|
||||||
|
};
|
||||||
|
|
||||||
|
setVal('bme-planner-api-channel', inferred.channel || 'openai');
|
||||||
|
setVal('bme-planner-prefix-mode', inferred.prefixMode || 'auto');
|
||||||
|
setVal('bme-planner-prefix-custom', inferred.customPrefix || '');
|
||||||
|
setVal('bme-planner-api-base', inferred.baseUrl || '');
|
||||||
|
setVal('bme-planner-api-key', inferred.apiKey || '');
|
||||||
|
setVal('bme-planner-model', inferred.model || '');
|
||||||
|
updatePrefixModeUI();
|
||||||
|
populatePlannerLlmPresetSelect(name);
|
||||||
|
}
|
||||||
|
|
||||||
/* ── Prompt block editor ────────────────────────────────────────────────── */
|
/* ── Prompt block editor ────────────────────────────────────────────────── */
|
||||||
|
|
||||||
function createPromptBlockElement(block, idx, total) {
|
function createPromptBlockElement(block, idx, total) {
|
||||||
@@ -364,6 +483,7 @@ function applyConfigToFields(cfg) {
|
|||||||
toBool(cfgCache.enabled, false) ? 'active' : 'idle',
|
toBool(cfgCache.enabled, false) ? 'active' : 'idle',
|
||||||
);
|
);
|
||||||
updatePrefixModeUI();
|
updatePrefixModeUI();
|
||||||
|
syncPlannerLlmPresetSelect();
|
||||||
|
|
||||||
const keepSelected = cfgCache.activePromptTemplate || $('bme-planner-tpl-select')?.value || '';
|
const keepSelected = cfgCache.activePromptTemplate || $('bme-planner-tpl-select')?.value || '';
|
||||||
renderTemplateSelect(keepSelected);
|
renderTemplateSelect(keepSelected);
|
||||||
@@ -539,6 +659,26 @@ function bindOnce(section) {
|
|||||||
if (!val) return;
|
if (!val) return;
|
||||||
const modelInput = $('bme-planner-model');
|
const modelInput = $('bme-planner-model');
|
||||||
if (modelInput) modelInput.value = val;
|
if (modelInput) modelInput.value = val;
|
||||||
|
syncPlannerLlmPresetSelect();
|
||||||
|
scheduleSave();
|
||||||
|
});
|
||||||
|
|
||||||
|
$('bme-planner-llm-preset-select')?.addEventListener('change', () => {
|
||||||
|
const select = $('bme-planner-llm-preset-select');
|
||||||
|
const selectedName = String(select?.value || '');
|
||||||
|
if (!selectedName) {
|
||||||
|
setLocalStatus('bme-planner-api-status', '', '');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { presets } = getSharedLlmPresetState();
|
||||||
|
const preset = presets?.[selectedName];
|
||||||
|
if (!preset) {
|
||||||
|
populatePlannerLlmPresetSelect('');
|
||||||
|
setLocalStatus('bme-planner-api-status', '选中的 BME 模板不存在,已切回手动模式', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
applyPlannerLlmPresetToFields(selectedName, preset);
|
||||||
|
setLocalStatus('bme-planner-api-status', `已套用 BME 模板:${selectedName}`, 'success');
|
||||||
scheduleSave();
|
scheduleSave();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -678,7 +818,9 @@ function bindOnce(section) {
|
|||||||
if (!target) return;
|
if (!target) return;
|
||||||
if (target.closest('.bme-planner-prompt-block')) return;
|
if (target.closest('.bme-planner-prompt-block')) return;
|
||||||
if (target.id === 'bme-planner-test-input') return;
|
if (target.id === 'bme-planner-test-input') return;
|
||||||
|
if (target.id === 'bme-planner-llm-preset-select') return;
|
||||||
if (!target.classList?.contains('bme-config-input')) return;
|
if (!target.classList?.contains('bme-config-input')) return;
|
||||||
|
syncPlannerLlmPresetSelect();
|
||||||
scheduleSave();
|
scheduleSave();
|
||||||
};
|
};
|
||||||
section.addEventListener('change', fieldChangeHandler);
|
section.addEventListener('change', fieldChangeHandler);
|
||||||
@@ -686,10 +828,13 @@ function bindOnce(section) {
|
|||||||
|
|
||||||
/* ── Public controller ──────────────────────────────────────────────────── */
|
/* ── Public controller ──────────────────────────────────────────────────── */
|
||||||
|
|
||||||
export function initPlannerSections(rootEl) {
|
export function initPlannerSections(rootEl, options = {}) {
|
||||||
const root = rootEl || document;
|
const root = rootEl || document;
|
||||||
const section = root.querySelector(SECTION_SELECTOR);
|
const section = root.querySelector(SECTION_SELECTOR);
|
||||||
if (!section) return;
|
if (!section) return;
|
||||||
|
if (typeof options.getSettings === 'function') {
|
||||||
|
externalGetSettings = options.getSettings;
|
||||||
|
}
|
||||||
bindOnce(section);
|
bindOnce(section);
|
||||||
|
|
||||||
const api = getPlannerApi();
|
const api = getPlannerApi();
|
||||||
@@ -719,7 +864,10 @@ export function initPlannerSections(rootEl) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function refreshPlannerSections() {
|
export function refreshPlannerSections(options = {}) {
|
||||||
|
if (typeof options.getSettings === 'function') {
|
||||||
|
externalGetSettings = options.getSettings;
|
||||||
|
}
|
||||||
const api = getPlannerApi();
|
const api = getPlannerApi();
|
||||||
if (!api) {
|
if (!api) {
|
||||||
setStatusChip('bme-planner-state-chip', '模块未加载', 'error');
|
setStatusChip('bme-planner-state-chip', '模块未加载', 'error');
|
||||||
@@ -750,5 +898,6 @@ export function cleanupPlannerSections() {
|
|||||||
cfgCache = null;
|
cfgCache = null;
|
||||||
logsCache = [];
|
logsCache = [];
|
||||||
fetchedModels = [];
|
fetchedModels = [];
|
||||||
|
externalGetSettings = null;
|
||||||
clearUndo();
|
clearUndo();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2924,6 +2924,20 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="bme-config-row bme-llm-preset-row">
|
||||||
|
<label for="bme-planner-llm-preset-select">复用 BME LLM 配置模板</label>
|
||||||
|
<div class="bme-llm-preset-controls">
|
||||||
|
<select
|
||||||
|
id="bme-planner-llm-preset-select"
|
||||||
|
class="bme-config-input"
|
||||||
|
>
|
||||||
|
<option value="">-- 手动模式 / 当前 ENA 配置 --</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="bme-config-help">
|
||||||
|
直接复用主面板的 LLM 预设,将 URL、Key、Model 拷贝到 ENA 规划器,并自动推断渠道与默认前缀;套用后仍可单独微调。
|
||||||
|
</div>
|
||||||
<div class="bme-config-row">
|
<div class="bme-config-row">
|
||||||
<label for="bme-planner-api-channel">渠道类型</label>
|
<label for="bme-planner-api-channel">渠道类型</label>
|
||||||
<select id="bme-planner-api-channel" class="bme-config-input">
|
<select id="bme-planner-api-channel" class="bme-config-input">
|
||||||
|
|||||||
@@ -1356,7 +1356,9 @@ function _switchTab(tabId) {
|
|||||||
|
|
||||||
function _refreshPlannerLauncher() {
|
function _refreshPlannerLauncher() {
|
||||||
try {
|
try {
|
||||||
refreshPlannerSections();
|
refreshPlannerSections({
|
||||||
|
getSettings: _getSettings,
|
||||||
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn("[ST-BME] planner section refresh failed:", err);
|
console.warn("[ST-BME] planner section refresh failed:", err);
|
||||||
}
|
}
|
||||||
@@ -1364,7 +1366,9 @@ function _refreshPlannerLauncher() {
|
|||||||
|
|
||||||
function _bindPlannerLauncher() {
|
function _bindPlannerLauncher() {
|
||||||
try {
|
try {
|
||||||
initPlannerSections(panelEl || document);
|
initPlannerSections(panelEl || document, {
|
||||||
|
getSettings: _getSettings,
|
||||||
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn("[ST-BME] planner section init failed:", err);
|
console.warn("[ST-BME] planner section init failed:", err);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user