改预设要能一边写一边看装出来的 messages,还要多模型对照,所以把编辑与试跑拆成两个视图塞进一个页面;版本存完整快照、可分叉,用例全局共用一份、运行记录按预设存。顺带让读预设文件先剥 BOM,静态资源改成不缓存,免得改了脚本还要强刷。
89 lines
2.7 KiB
TypeScript
89 lines
2.7 KiB
TypeScript
import { readdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
||
import path from "node:path";
|
||
import type { PresetPackage } from "../types/preset.js";
|
||
import { ensureUserDataDirs, getPresetsDir } from "../config/user-data-dir.js";
|
||
import { importSillyTavernPreset } from "./importer.js";
|
||
import { clearToolPlanCache } from "./tool-policy.js";
|
||
|
||
/** 旧预设里的思考强度不再使用;读到就从文件里删掉。 */
|
||
function withoutReasoningEffort(preset: PresetPackage, filePath: string): PresetPackage {
|
||
if (preset.generation?.reasoningEffort === undefined) return preset;
|
||
const generation = { ...preset.generation };
|
||
delete generation.reasoningEffort;
|
||
const next = { ...preset, generation };
|
||
writeFileSync(filePath, JSON.stringify(next, null, 2), "utf8");
|
||
return next;
|
||
}
|
||
|
||
function presetPath(id: string): string {
|
||
return path.join(getPresetsDir(), `${id}.json`);
|
||
}
|
||
|
||
/** Windows 编辑器可能给 JSON 加 BOM;JSON.parse 不认,先剥掉。 */
|
||
function parsePreset(raw: string): PresetPackage {
|
||
return JSON.parse(raw.replace(/^\uFEFF/, "")) as PresetPackage;
|
||
}
|
||
|
||
export function listPresets(): PresetPackage[] {
|
||
ensureUserDataDirs();
|
||
let files: string[];
|
||
try {
|
||
files = readdirSync(getPresetsDir()).filter((f) => f.endsWith(".json"));
|
||
} catch {
|
||
return [];
|
||
}
|
||
|
||
const presets: PresetPackage[] = [];
|
||
for (const file of files) {
|
||
try {
|
||
const filePath = path.join(getPresetsDir(), file);
|
||
const raw = readFileSync(filePath, "utf8");
|
||
presets.push(withoutReasoningEffort(parsePreset(raw), filePath));
|
||
} catch {
|
||
/* skip corrupt files */
|
||
}
|
||
}
|
||
return presets.sort((a, b) => a.name.localeCompare(b.name, "zh-CN"));
|
||
}
|
||
|
||
export function getPreset(id: string): PresetPackage | null {
|
||
try {
|
||
const filePath = presetPath(id);
|
||
const raw = readFileSync(filePath, "utf8");
|
||
return withoutReasoningEffort(parsePreset(raw), filePath);
|
||
} catch {
|
||
return null;
|
||
}
|
||
}
|
||
|
||
export function savePreset(preset: PresetPackage): PresetPackage {
|
||
ensureUserDataDirs();
|
||
writeFileSync(presetPath(preset.id), JSON.stringify(preset, null, 2), "utf8");
|
||
// 条目/内容被改过,导入时算好的工具映射要重建
|
||
clearToolPlanCache(preset.id);
|
||
return preset;
|
||
}
|
||
|
||
export function deletePreset(id: string): void {
|
||
clearToolPlanCache(id);
|
||
try {
|
||
unlinkSync(presetPath(id));
|
||
} catch {
|
||
/* ignore */
|
||
}
|
||
}
|
||
|
||
export function importAndSavePreset(
|
||
raw: unknown,
|
||
options: { name?: string } = {},
|
||
) {
|
||
const report = importSillyTavernPreset(raw, options);
|
||
savePreset(report.preset);
|
||
return report;
|
||
}
|
||
|
||
export function resolveActivePreset(activePresetId: string | null): PresetPackage | null {
|
||
if (!activePresetId) return null;
|
||
return getPreset(activePresetId);
|
||
}
|