-
Chat Completion Presets
-
Text Completion presets
-
@@ -2150,7 +2161,7 @@
-
+
@@ -2241,7 +2252,7 @@
-
+
diff --git a/public/scripts/preset-manager.js b/public/scripts/preset-manager.js
index 2505d22bd..760a364b6 100644
--- a/public/scripts/preset-manager.js
+++ b/public/scripts/preset-manager.js
@@ -117,10 +117,11 @@ class PresetManager {
}
async savePresetAs() {
+ const inputValue = this.getSelectedPresetName();
const popupText = `
Preset name:
${!this.isNonGenericApi() ? '
Hint: Use a character/group name to bind preset to a specific chat.
' : ''}`;
- const name = await callPopup(popupText, 'input');
+ const name = await callPopup(popupText, 'input', inputValue);
if (!name) {
console.log('Preset name not provided');
@@ -314,11 +315,22 @@ class PresetManager {
body: JSON.stringify({ name: nameToDelete, apiId: this.apiId }),
});
+ return response.ok;
+ }
+
+ async getDefaultPreset(name) {
+ const response = await fetch('/api/presets/restore', {
+ method: 'POST',
+ headers: getRequestHeaders(),
+ body: JSON.stringify({ name, apiId: this.apiId }),
+ });
+
if (!response.ok) {
- toastr.warning('Preset was not deleted from server');
- } else {
- toastr.success('Preset deleted');
+ toastr.error('Failed to restore default preset');
+ return;
}
+
+ return await response.json();
}
}
@@ -417,7 +429,65 @@ jQuery(async () => {
return;
}
- await presetManager.deleteCurrentPreset();
+ const result = await presetManager.deleteCurrentPreset();
+
+ if (result) {
+ toastr.success('Preset deleted');
+ } else {
+ toastr.warning('Preset was not deleted from server');
+ }
+
saveSettingsDebounced();
});
+
+ $(document).on('click', '[data-preset-manager-restore]', async function() {
+ const apiId = $(this).data('preset-manager-restore');
+ const presetManager = getPresetManager(apiId);
+
+ if (!presetManager) {
+ console.warn(`Preset Manager not found for API: ${apiId}`);
+ return;
+ }
+
+ const name = presetManager.getSelectedPresetName();
+ const data = await presetManager.getDefaultPreset(name);
+
+ if (name == 'gui') {
+ toastr.info('Cannot restore GUI preset');
+ return;
+ }
+
+ if (!data) {
+ return;
+ }
+
+ if (data.isDefault) {
+ if (Object.keys(data.preset).length === 0) {
+ toastr.error('Default preset cannot be restored');
+ return;
+ }
+
+ const confirm = await callPopup('
Are you sure?
Resetting a
default preset will restore the default settings.', 'confirm');
+
+ if (!confirm) {
+ return;
+ }
+
+ await presetManager.deleteCurrentPreset();
+ await presetManager.savePreset(name, data.preset);
+ const option = presetManager.findPreset(name);
+ presetManager.selectPreset(option);
+ toastr.success('Default preset restored');
+ } else {
+ const confirm = await callPopup('
Are you sure?
Resetting a
custom preset will restore to the last saved state.', 'confirm');
+
+ if (!confirm) {
+ return;
+ }
+
+ const option = presetManager.findPreset(name);
+ presetManager.selectPreset(option);
+ toastr.success('Preset restored');
+ }
+ });
});
diff --git a/src/content-manager.js b/src/content-manager.js
index f97225b39..955e9fc63 100644
--- a/src/content-manager.js
+++ b/src/content-manager.js
@@ -7,11 +7,56 @@ const contentDirectory = path.join(process.cwd(), 'default/content');
const contentLogPath = path.join(contentDirectory, 'content.log');
const contentIndexPath = path.join(contentDirectory, 'index.json');
const { DIRECTORIES } = require('./constants');
+const presetFolders = [DIRECTORIES.koboldAI_Settings, DIRECTORIES.openAI_Settings, DIRECTORIES.novelAI_Settings, DIRECTORIES.textGen_Settings];
+/**
+ * Gets the default presets from the content directory.
+ * @returns {object[]} Array of default presets
+ */
+function getDefaultPresets() {
+ try {
+ const contentIndexText = fs.readFileSync(contentIndexPath, 'utf8');
+ const contentIndex = JSON.parse(contentIndexText);
+
+ const presets = [];
+
+ for (const contentItem of contentIndex) {
+ if (contentItem.type.endsWith('_preset')) {
+ contentItem.name = path.parse(contentItem.filename).name;
+ contentItem.folder = getTargetByType(contentItem.type);
+ presets.push(contentItem);
+ }
+ }
+
+ return presets;
+ } catch (err) {
+ console.log('Failed to get default presets', err);
+ return [];
+ }
+}
+
+/**
+ * Gets a default JSON file from the content directory.
+ * @param {string} filename Name of the file to get
+ * @returns {object | null} JSON object or null if the file doesn't exist
+ */
+function getDefaultPresetFile(filename) {
+ try {
+ const contentPath = path.join(contentDirectory, filename);
+
+ if (!fs.existsSync(contentPath)) {
+ return null;
+ }
+
+ const fileContent = fs.readFileSync(contentPath, 'utf8');
+ return JSON.parse(fileContent);
+ } catch (err) {
+ console.log(`Failed to get default file ${filename}`, err);
+ return null;
+ }
+}
function migratePresets() {
- const presetFolders = [DIRECTORIES.koboldAI_Settings, DIRECTORIES.openAI_Settings, DIRECTORIES.novelAI_Settings, DIRECTORIES.textGen_Settings];
-
for (const presetFolder of presetFolders) {
const presetPath = path.join(process.cwd(), presetFolder);
const presetFiles = fs.readdirSync(presetPath);
@@ -20,9 +65,12 @@ function migratePresets() {
const presetFilePath = path.join(presetPath, presetFile);
const newFileName = presetFile.replace('.settings', '.json');
const newFilePath = path.join(presetPath, newFileName);
+ const backupFileName = presetFolder.replace('/', '_') + '_' + presetFile;
+ const backupFilePath = path.join(DIRECTORIES.backups, backupFileName);
if (presetFilePath.endsWith('.settings')) {
if (!fs.existsSync(newFilePath)) {
+ fs.cpSync(presetFilePath, backupFilePath);
fs.cpSync(presetFilePath, newFilePath);
console.log(`Migrated ${presetFilePath} to ${newFilePath}`);
}
@@ -310,4 +358,6 @@ function registerEndpoints(app, jsonParser) {
module.exports = {
checkForNewContent,
registerEndpoints,
+ getDefaultPresets,
+ getDefaultPresetFile,
};
diff --git a/src/presets.js b/src/presets.js
index eee9034c7..24eecf917 100644
--- a/src/presets.js
+++ b/src/presets.js
@@ -3,6 +3,7 @@ const path = require('path');
const sanitize = require('sanitize-filename');
const writeFileAtomicSync = require('write-file-atomic').sync;
const { DIRECTORIES } = require('./constants');
+const { getDefaultPresetFile, getDefaultPresets } = require('./content-manager');
/**
* Gets the folder and extension for the preset settings based on the API source ID.
@@ -76,6 +77,28 @@ function registerEndpoints(app, jsonParser) {
}
});
+ app.post('/api/presets/restore', jsonParser, function (request, response) {
+ try {
+ const settings = getPresetSettingsByAPI(request.body.apiId);
+ const name = sanitize(request.body.name);
+ const defaultPresets = getDefaultPresets();
+
+ const defaultPreset = defaultPresets.find(p => p.name === name && p.folder === settings.folder);
+
+ const result = { isDefault: false, preset: {} };
+
+ if (defaultPreset) {
+ result.isDefault = true;
+ result.preset = getDefaultPresetFile(defaultPreset.filename) || {};
+ }
+
+ return response.send(result);
+ } catch (error) {
+ console.log(error);
+ return response.sendStatus(500);
+ }
+ });
+
// TODO: Merge with /api/presets/save
app.post('/api/presets/save-openai', jsonParser, function (request, response) {
if (!request.body || typeof request.query.name !== 'string') return response.sendStatus(400);