mirror of
https://github.com/Youzini-afk/ST-Bionic-Memory-Ecology.git
synced 2026-05-15 22:30:38 +08:00
Fix memory LLM preset auto detection
This commit is contained in:
110
llm-preset-utils.js
Normal file
110
llm-preset-utils.js
Normal file
@@ -0,0 +1,110 @@
|
||||
function normalizeLlmConfigValue(value) {
|
||||
return String(value || "").trim();
|
||||
}
|
||||
|
||||
export function isSameLlmConfigSnapshot(left = {}, right = {}) {
|
||||
return (
|
||||
normalizeLlmConfigValue(left?.llmApiUrl) ===
|
||||
normalizeLlmConfigValue(right?.llmApiUrl) &&
|
||||
normalizeLlmConfigValue(left?.llmApiKey) ===
|
||||
normalizeLlmConfigValue(right?.llmApiKey) &&
|
||||
normalizeLlmConfigValue(left?.llmModel) ===
|
||||
normalizeLlmConfigValue(right?.llmModel)
|
||||
);
|
||||
}
|
||||
|
||||
export function normalizeLlmPresetMap(rawPresets = {}) {
|
||||
const normalizedPresets = {};
|
||||
let changed =
|
||||
!rawPresets ||
|
||||
typeof rawPresets !== "object" ||
|
||||
Array.isArray(rawPresets);
|
||||
|
||||
if (!changed) {
|
||||
for (const [name, preset] of Object.entries(rawPresets)) {
|
||||
const normalizedName = String(name || "").trim();
|
||||
if (!normalizedName) {
|
||||
changed = true;
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
!preset ||
|
||||
typeof preset !== "object" ||
|
||||
Array.isArray(preset) ||
|
||||
typeof preset.llmApiUrl !== "string" ||
|
||||
typeof preset.llmApiKey !== "string" ||
|
||||
typeof preset.llmModel !== "string"
|
||||
) {
|
||||
changed = true;
|
||||
continue;
|
||||
}
|
||||
normalizedPresets[normalizedName] = {
|
||||
llmApiUrl: normalizeLlmConfigValue(preset.llmApiUrl),
|
||||
llmApiKey: normalizeLlmConfigValue(preset.llmApiKey),
|
||||
llmModel: normalizeLlmConfigValue(preset.llmModel),
|
||||
};
|
||||
if (normalizedName !== name) {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
presets: normalizedPresets,
|
||||
changed,
|
||||
};
|
||||
}
|
||||
|
||||
export function sanitizeLlmPresetSettings(settings = {}) {
|
||||
const normalized = settings && typeof settings === "object" ? settings : {};
|
||||
const { presets, changed: presetChanged } = normalizeLlmPresetMap(
|
||||
normalized.llmPresets,
|
||||
);
|
||||
let activePreset =
|
||||
typeof normalized.llmActivePreset === "string"
|
||||
? normalized.llmActivePreset
|
||||
: "";
|
||||
let changed = presetChanged || typeof normalized.llmActivePreset !== "string";
|
||||
|
||||
if (
|
||||
activePreset &&
|
||||
!Object.prototype.hasOwnProperty.call(presets, activePreset)
|
||||
) {
|
||||
activePreset = "";
|
||||
changed = true;
|
||||
}
|
||||
|
||||
return {
|
||||
presets,
|
||||
activePreset,
|
||||
changed,
|
||||
};
|
||||
}
|
||||
|
||||
export function resolveActiveLlmPresetName(settings = {}) {
|
||||
const normalized = settings && typeof settings === "object" ? settings : {};
|
||||
const { presets, activePreset } = sanitizeLlmPresetSettings(normalized);
|
||||
const snapshot = {
|
||||
llmApiUrl: normalizeLlmConfigValue(normalized.llmApiUrl),
|
||||
llmApiKey: normalizeLlmConfigValue(normalized.llmApiKey),
|
||||
llmModel: normalizeLlmConfigValue(normalized.llmModel),
|
||||
};
|
||||
|
||||
if (
|
||||
activePreset &&
|
||||
presets[activePreset] &&
|
||||
isSameLlmConfigSnapshot(snapshot, presets[activePreset])
|
||||
) {
|
||||
return activePreset;
|
||||
}
|
||||
|
||||
const matchingPresets = Object.keys(presets).filter((name) =>
|
||||
isSameLlmConfigSnapshot(snapshot, presets[name]),
|
||||
);
|
||||
|
||||
if (matchingPresets.length === 1) {
|
||||
return matchingPresets[0];
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
99
panel.js
99
panel.js
@@ -10,6 +10,10 @@ import {
|
||||
buildScopeBadgeText,
|
||||
normalizeMemoryScope,
|
||||
} from "./memory-scope.js";
|
||||
import {
|
||||
resolveActiveLlmPresetName,
|
||||
sanitizeLlmPresetSettings,
|
||||
} from "./llm-preset-utils.js";
|
||||
import {
|
||||
cloneTaskProfile,
|
||||
createBuiltinPromptBlock,
|
||||
@@ -1866,11 +1870,8 @@ function _bindActions() {
|
||||
}
|
||||
|
||||
function _refreshConfigTab() {
|
||||
let settings = _normalizeLlmPresetSettings(_getSettings?.() || {});
|
||||
const resolvedActiveLlmPreset = _resolveActiveLlmPresetName(settings);
|
||||
if (resolvedActiveLlmPreset !== String(settings.llmActivePreset || "")) {
|
||||
settings = _patchSettings({ llmActivePreset: resolvedActiveLlmPreset });
|
||||
}
|
||||
const settings = _resolveAndPersistActiveLlmPreset(_getSettings?.() || {});
|
||||
const resolvedActiveLlmPreset = String(settings.llmActivePreset || "");
|
||||
_refreshPlannerLauncher();
|
||||
|
||||
_setCheckboxValue("bme-setting-enabled", settings.enabled ?? true);
|
||||
@@ -5736,81 +5737,28 @@ function _patchSettings(patch = {}, options = {}) {
|
||||
}
|
||||
|
||||
function _normalizeLlmPresetSettings(settings = _getSettings?.() || {}) {
|
||||
const rawPresets = settings?.llmPresets;
|
||||
const normalizedPresets = {};
|
||||
let changed =
|
||||
!rawPresets ||
|
||||
typeof rawPresets !== "object" ||
|
||||
Array.isArray(rawPresets);
|
||||
const normalized = sanitizeLlmPresetSettings(settings);
|
||||
|
||||
if (!changed) {
|
||||
for (const [name, preset] of Object.entries(rawPresets)) {
|
||||
if (!String(name || "").trim()) {
|
||||
changed = true;
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
!preset ||
|
||||
typeof preset !== "object" ||
|
||||
Array.isArray(preset) ||
|
||||
typeof preset.llmApiUrl !== "string" ||
|
||||
typeof preset.llmApiKey !== "string" ||
|
||||
typeof preset.llmModel !== "string"
|
||||
) {
|
||||
changed = true;
|
||||
continue;
|
||||
}
|
||||
normalizedPresets[name] = {
|
||||
llmApiUrl: preset.llmApiUrl,
|
||||
llmApiKey: preset.llmApiKey,
|
||||
llmModel: preset.llmModel,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let activePreset =
|
||||
typeof settings?.llmActivePreset === "string" ? settings.llmActivePreset : "";
|
||||
if (activePreset && !Object.prototype.hasOwnProperty.call(normalizedPresets, activePreset)) {
|
||||
activePreset = "";
|
||||
changed = true;
|
||||
}
|
||||
if (typeof settings?.llmActivePreset !== "string") {
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (!changed) {
|
||||
if (!normalized.changed) {
|
||||
return settings;
|
||||
}
|
||||
|
||||
return _patchSettings({
|
||||
llmPresets: normalizedPresets,
|
||||
llmActivePreset: activePreset,
|
||||
llmPresets: normalized.presets,
|
||||
llmActivePreset: normalized.activePreset,
|
||||
});
|
||||
}
|
||||
|
||||
function _resolveActiveLlmPresetName(settings = _getSettings?.() || {}) {
|
||||
const activePreset = String(settings?.llmActivePreset || "");
|
||||
if (!activePreset) return "";
|
||||
const preset = settings?.llmPresets?.[activePreset];
|
||||
if (!preset) return "";
|
||||
return _isSameLlmConfigSnapshot(
|
||||
{
|
||||
llmApiUrl: String(settings?.llmApiUrl || ""),
|
||||
llmApiKey: String(settings?.llmApiKey || ""),
|
||||
llmModel: String(settings?.llmModel || ""),
|
||||
},
|
||||
preset,
|
||||
)
|
||||
? activePreset
|
||||
: "";
|
||||
}
|
||||
|
||||
function _isSameLlmConfigSnapshot(left = {}, right = {}) {
|
||||
return (
|
||||
String(left?.llmApiUrl || "") === String(right?.llmApiUrl || "") &&
|
||||
String(left?.llmApiKey || "") === String(right?.llmApiKey || "") &&
|
||||
String(left?.llmModel || "") === String(right?.llmModel || "")
|
||||
);
|
||||
function _resolveAndPersistActiveLlmPreset(settings = _getSettings?.() || {}) {
|
||||
const normalizedSettings = _normalizeLlmPresetSettings(settings);
|
||||
const resolvedActivePreset = resolveActiveLlmPresetName(normalizedSettings);
|
||||
if (
|
||||
resolvedActivePreset !==
|
||||
String(normalizedSettings?.llmActivePreset || "")
|
||||
) {
|
||||
return _patchSettings({ llmActivePreset: resolvedActivePreset });
|
||||
}
|
||||
return normalizedSettings;
|
||||
}
|
||||
|
||||
function _getLlmConfigInputSnapshot() {
|
||||
@@ -5877,11 +5825,8 @@ function _markLlmPresetDirty(options = {}) {
|
||||
_clearFetchedLlmModels();
|
||||
}
|
||||
|
||||
const activePreset = String((_getSettings?.() || {}).llmActivePreset || "");
|
||||
if (activePreset) {
|
||||
_patchSettings({ llmActivePreset: "" });
|
||||
}
|
||||
_syncLlmPresetControls("");
|
||||
const settings = _resolveAndPersistActiveLlmPreset(_getSettings?.() || {});
|
||||
_syncLlmPresetControls(String(settings?.llmActivePreset || ""));
|
||||
}
|
||||
|
||||
function _highlightThemeChoice(themeName) {
|
||||
|
||||
134
tests/llm-preset-utils.mjs
Normal file
134
tests/llm-preset-utils.mjs
Normal file
@@ -0,0 +1,134 @@
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import {
|
||||
isSameLlmConfigSnapshot,
|
||||
normalizeLlmPresetMap,
|
||||
resolveActiveLlmPresetName,
|
||||
sanitizeLlmPresetSettings,
|
||||
} from "../llm-preset-utils.js";
|
||||
|
||||
assert.equal(
|
||||
isSameLlmConfigSnapshot(
|
||||
{
|
||||
llmApiUrl: " https://example.com/v1 ",
|
||||
llmApiKey: " sk-test ",
|
||||
llmModel: " model-a ",
|
||||
},
|
||||
{
|
||||
llmApiUrl: "https://example.com/v1",
|
||||
llmApiKey: "sk-test",
|
||||
llmModel: "model-a",
|
||||
},
|
||||
),
|
||||
true,
|
||||
);
|
||||
|
||||
const normalizedMap = normalizeLlmPresetMap({
|
||||
Alpha: {
|
||||
llmApiUrl: " https://example.com/v1 ",
|
||||
llmApiKey: " sk-alpha ",
|
||||
llmModel: " model-a ",
|
||||
},
|
||||
"": {
|
||||
llmApiUrl: "https://bad.example/v1",
|
||||
llmApiKey: "sk-bad",
|
||||
llmModel: "bad-model",
|
||||
},
|
||||
Broken: {
|
||||
llmApiUrl: "https://broken.example/v1",
|
||||
llmApiKey: 42,
|
||||
llmModel: "broken",
|
||||
},
|
||||
});
|
||||
assert.equal(normalizedMap.changed, true);
|
||||
assert.deepEqual(normalizedMap.presets, {
|
||||
Alpha: {
|
||||
llmApiUrl: "https://example.com/v1",
|
||||
llmApiKey: "sk-alpha",
|
||||
llmModel: "model-a",
|
||||
},
|
||||
});
|
||||
|
||||
const sanitized = sanitizeLlmPresetSettings({
|
||||
llmPresets: {
|
||||
Alpha: {
|
||||
llmApiUrl: "https://example.com/v1",
|
||||
llmApiKey: "sk-alpha",
|
||||
llmModel: "model-a",
|
||||
},
|
||||
},
|
||||
llmActivePreset: "Missing",
|
||||
});
|
||||
assert.equal(sanitized.changed, true);
|
||||
assert.equal(sanitized.activePreset, "");
|
||||
|
||||
const uniqueMatchSettings = {
|
||||
llmApiUrl: "https://example.com/v1",
|
||||
llmApiKey: "sk-alpha",
|
||||
llmModel: "model-a",
|
||||
llmPresets: {
|
||||
Alpha: {
|
||||
llmApiUrl: "https://example.com/v1",
|
||||
llmApiKey: "sk-alpha",
|
||||
llmModel: "model-a",
|
||||
},
|
||||
Beta: {
|
||||
llmApiUrl: "https://example.com/v1",
|
||||
llmApiKey: "sk-beta",
|
||||
llmModel: "model-b",
|
||||
},
|
||||
},
|
||||
llmActivePreset: "",
|
||||
};
|
||||
assert.equal(resolveActiveLlmPresetName(uniqueMatchSettings), "Alpha");
|
||||
|
||||
const preservedActiveSettings = {
|
||||
llmApiUrl: "https://example.com/v1",
|
||||
llmApiKey: "sk-shared",
|
||||
llmModel: "shared-model",
|
||||
llmPresets: {
|
||||
Alpha: {
|
||||
llmApiUrl: "https://example.com/v1",
|
||||
llmApiKey: "sk-shared",
|
||||
llmModel: "shared-model",
|
||||
},
|
||||
Beta: {
|
||||
llmApiUrl: "https://example.com/v1",
|
||||
llmApiKey: "sk-shared",
|
||||
llmModel: "shared-model",
|
||||
},
|
||||
},
|
||||
llmActivePreset: "Beta",
|
||||
};
|
||||
assert.equal(resolveActiveLlmPresetName(preservedActiveSettings), "Beta");
|
||||
|
||||
const ambiguousSettings = {
|
||||
llmApiUrl: "https://example.com/v1",
|
||||
llmApiKey: "sk-shared",
|
||||
llmModel: "shared-model",
|
||||
llmPresets: {
|
||||
Alpha: {
|
||||
llmApiUrl: "https://example.com/v1",
|
||||
llmApiKey: "sk-shared",
|
||||
llmModel: "shared-model",
|
||||
},
|
||||
Beta: {
|
||||
llmApiUrl: "https://example.com/v1",
|
||||
llmApiKey: "sk-shared",
|
||||
llmModel: "shared-model",
|
||||
},
|
||||
},
|
||||
llmActivePreset: "",
|
||||
};
|
||||
assert.equal(resolveActiveLlmPresetName(ambiguousSettings), "");
|
||||
|
||||
const noMatchSettings = {
|
||||
llmApiUrl: "https://example.com/v1",
|
||||
llmApiKey: "sk-gamma",
|
||||
llmModel: "model-gamma",
|
||||
llmPresets: uniqueMatchSettings.llmPresets,
|
||||
llmActivePreset: "",
|
||||
};
|
||||
assert.equal(resolveActiveLlmPresetName(noMatchSettings), "");
|
||||
|
||||
console.log("llm-preset-utils tests passed");
|
||||
Reference in New Issue
Block a user