Add /preset command

This commit is contained in:
Cohee 2023-12-12 19:14:17 +02:00
parent 2ca9015a5f
commit 9176f46caf
4 changed files with 169 additions and 57 deletions

View File

@ -177,7 +177,7 @@
<div>
<h4 class="margin0"><span data-i18n="openaipresets">Chat Completion Presets</span></h4>
<div class="flex-container flexNoGap">
<select id="settings_preset_openai" class="flex1 text_pole">
<select id="settings_preset_openai" class="flex1 text_pole" data-preset-manager-for="openai">
<option value="gui" data-i18n="default">Default</option>
</select>
<div class="flex-container flexBasis100p justifyCenter">

View File

@ -192,6 +192,7 @@ import { BulkEditOverlay, CharacterContextMenu } from './scripts/BulkEditOverlay
import { loadMancerModels } from './scripts/mancer-settings.js';
import { appendFileContent, hasPendingFileAttachment, populateFileAttachment } from './scripts/chats.js';
import { replaceVariableMacros } from './scripts/variables.js';
import { initPresetManager } from './scripts/preset-manager.js';
//exporting functions and vars for mods
export {
@ -738,6 +739,7 @@ async function firstLoadInit() {
await getCharacters();
await getBackgrounds();
await initTokenizers();
await initPresetManager();
initBackgrounds();
initAuthorsNote();
initPersonas();
@ -7446,7 +7448,10 @@ const swipe_right = () => {
}
};
function connectAPISlash(_, text) {
/**
* @param {string} text API name
*/
async function connectAPISlash(_, text) {
if (!text) return;
const apiMap = {
@ -7460,7 +7465,29 @@ function connectAPISlash(_, text) {
button: '#api_button_novel',
},
'ooba': {
selected: 'textgenerationwebui',
button: '#api_button_textgenerationwebui',
type: textgen_types.OOBA,
},
'tabby': {
selected: 'textgenerationwebui',
button: '#api_button_textgenerationwebui',
type: textgen_types.TABBY,
},
'mancer': {
selected: 'textgenerationwebui',
button: '#api_button_textgenerationwebui',
type: textgen_types.MANCER,
},
'aphrodite': {
selected: 'textgenerationwebui',
button: '#api_button_textgenerationwebui',
type: textgen_types.APHRODITE,
},
'kcpp': {
selected: 'textgenerationwebui',
button: '#api_button_textgenerationwebui',
type: textgen_types.KOBOLDCPP,
},
'oai': {
selected: 'openai',
@ -7499,7 +7526,7 @@ function connectAPISlash(_, text) {
},
};
const apiConfig = apiMap[text];
const apiConfig = apiMap[text.toLowerCase()];
if (!apiConfig) {
toastr.error(`Error: ${text} is not a valid API`);
return;
@ -7513,11 +7540,23 @@ function connectAPISlash(_, text) {
$('#chat_completion_source').trigger('change');
}
if (apiConfig.type) {
$(`#textgen_type option[value='${apiConfig.type}']`).prop('selected', true);
$('#textgen_type').trigger('change');
}
if (apiConfig.button) {
$(apiConfig.button).trigger('click');
}
toastr.info(`API set to ${text}, trying to connect..`);
try {
await waitUntilCondition(() => online_status !== 'no_connection', 5000, 100);
console.log('Connection successful');
} catch {
console.log('Could not connect after 5 seconds, skipping.');
}
}
export async function processDroppedFiles(files) {
@ -7771,7 +7810,7 @@ jQuery(async function () {
}
registerSlashCommand('dupe', DupeChar, [], ' duplicates the currently selected character', true, true);
registerSlashCommand('api', connectAPISlash, [], '<span class="monospace">(kobold, horde, novel, ooba, oai, claude, windowai, openrouter, scale, ai21, palm)</span> connect to an API', true, true);
registerSlashCommand('api', connectAPISlash, [], '<span class="monospace">(kobold, horde, novel, ooba, tabby, mancer, aphrodite, kcpp, oai, claude, windowai, openrouter, scale, ai21, palm)</span> connect to an API', true, true);
registerSlashCommand('impersonate', doImpersonate, ['imp'], ' calls an impersonation response', true, true);
registerSlashCommand('delchat', doDeleteChat, [], ' deletes the current chat', true, true);
registerSlashCommand('closechat', doCloseChat, [], ' closes the current chat', true, true);

View File

@ -2478,28 +2478,6 @@ function showWindowExtensionError() {
});
}
function trySelectPresetByName(name) {
let preset_found = null;
for (const key in openai_setting_names) {
if (name.trim() == key.trim()) {
preset_found = key;
break;
}
}
// Don't change if the current preset is the same
if (preset_found && preset_found === oai_settings.preset_settings_openai) {
return;
}
if (preset_found) {
oai_settings.preset_settings_openai = preset_found;
const value = openai_setting_names[preset_found];
$(`#settings_preset_openai option[value="${value}"]`).attr('selected', true);
$('#settings_preset_openai').val(value).trigger('change');
}
}
/**
* Persist a settings preset with the given name
*
@ -3573,29 +3551,6 @@ $(document).ready(async function () {
saveSettingsDebounced();
});
// auto-select a preset based on character/group name
$(document).on('click', '.character_select', function () {
const chid = $(this).attr('chid');
const name = characters[chid]?.name;
if (!name) {
return;
}
trySelectPresetByName(name);
});
$(document).on('click', '.group_select', function () {
const grid = $(this).data('id');
const name = groups.find(x => x.id === grid)?.name;
if (!name) {
return;
}
trySelectPresetByName(name);
});
$('#update_oai_preset').on('click', async function () {
const name = oai_settings.preset_settings_openai;
await saveOpenAIPreset(name, oai_settings);

View File

@ -12,6 +12,7 @@ import {
nai_settings,
novelai_setting_names,
novelai_settings,
online_status,
saveSettingsDebounced,
this_chid,
} from '../script.js';
@ -19,6 +20,7 @@ import { groups, selected_group } from './group-chats.js';
import { instruct_presets } from './instruct-mode.js';
import { kai_settings } from './kai-settings.js';
import { context_presets, getContextSettings, power_user } from './power-user.js';
import { registerSlashCommand } from './slash-commands.js';
import {
textgenerationwebui_preset_names,
textgenerationwebui_presets,
@ -28,6 +30,9 @@ import { download, parseJsonFile, waitUntilCondition } from './utils.js';
const presetManagers = {};
/**
* Automatically select a preset for current API based on character or group name.
*/
function autoSelectPreset() {
const presetManager = getPresetManager();
@ -57,7 +62,12 @@ function autoSelectPreset() {
}
}
function getPresetManager(apiId) {
/**
* Gets a preset manager by API id.
* @param {string} apiId API id
* @returns {PresetManager} Preset manager
*/
function getPresetManager(apiId = '') {
if (!apiId) {
apiId = main_api == 'koboldhorde' ? 'kobold' : main_api;
}
@ -69,6 +79,9 @@ function getPresetManager(apiId) {
return presetManagers[apiId];
}
/**
* Registers preset managers for all select elements with data-preset-manager-for attribute.
*/
function registerPresetManagers() {
$('select[data-preset-manager-for]').each((_, e) => {
const forData = $(e).data('preset-manager-for');
@ -85,21 +98,46 @@ class PresetManager {
this.apiId = apiId;
}
/**
* Gets all preset names.
* @returns {string[]} List of preset names
*/
getAllPresets() {
return $(this.select).find('option').map((_, el) => el.text).toArray();
}
/**
* Finds a preset by name.
* @param {string} name Preset name
* @returns {any} Preset value
*/
findPreset(name) {
return $(this.select).find(`option:contains(${name})`).val();
}
/**
* Gets the selected preset value.
* @returns {any} Selected preset value
*/
getSelectedPreset() {
return $(this.select).find('option:selected').val();
}
/**
* Gets the selected preset name.
* @returns {string} Selected preset name
*/
getSelectedPresetName() {
return $(this.select).find('option:selected').text();
}
selectPreset(preset) {
$(this.select).find(`option[value=${preset}]`).prop('selected', true);
$(this.select).val(preset).trigger('change');
/**
* Selects a preset by option value.
* @param {string} value Preset option value
*/
selectPreset(value) {
$(this.select).find(`option[value=${value}]`).prop('selected', true);
$(this.select).val(value).trigger('change');
}
async updatePreset() {
@ -334,11 +372,91 @@ class PresetManager {
}
}
jQuery(async () => {
await waitUntilCondition(() => eventSource !== undefined);
/**
* Selects a preset by name for current API.
* @param {any} _ Named arguments
* @param {string} name Unnamed arguments
* @returns {Promise<string>} Selected or current preset name
*/
async function presetCommandCallback(_, name) {
const shouldReconnect = online_status !== 'no_connection';
const presetManager = getPresetManager();
const allPresets = presetManager.getAllPresets();
const currentPreset = presetManager.getSelectedPresetName();
if (!presetManager) {
console.debug(`Preset Manager not found for API: ${main_api}`);
return '';
}
if (!name) {
console.log('No name provided for /preset command, using current preset');
return currentPreset;
}
if (!Array.isArray(allPresets) || allPresets.length === 0) {
console.log(`No presets found for API: ${main_api}`);
return currentPreset;
}
// Find exact match
const exactMatch = allPresets.find(p => p.toLowerCase().trim() === name.toLowerCase().trim());
if (exactMatch) {
console.log('Found exact preset match', exactMatch);
if (currentPreset !== exactMatch) {
const presetValue = presetManager.findPreset(exactMatch);
if (presetValue) {
presetManager.selectPreset(presetValue);
shouldReconnect && await waitForConnection();
}
}
return exactMatch;
} else {
// Find fuzzy match
const fuse = new Fuse(allPresets);
const fuzzyMatch = fuse.search(name);
if (!fuzzyMatch.length) {
console.warn(`WARN: Preset found with name ${name}`);
return currentPreset;
}
const fuzzyPresetName = fuzzyMatch[0].item;
const fuzzyPresetValue = presetManager.findPreset(fuzzyPresetName);
if (fuzzyPresetValue) {
console.log('Found fuzzy preset match', fuzzyPresetName);
if (currentPreset !== fuzzyPresetName) {
presetManager.selectPreset(fuzzyPresetValue);
shouldReconnect && await waitForConnection();
}
}
return fuzzyPresetName;
}
}
/**
* Waits for API connection to be established.
*/
async function waitForConnection() {
try {
await waitUntilCondition(() => online_status !== 'no_connection', 5000, 100);
} catch {
console.log('Timeout waiting for API to connect');
}
}
export async function initPresetManager() {
eventSource.on(event_types.CHAT_CHANGED, autoSelectPreset);
registerPresetManagers();
registerSlashCommand('preset', presetCommandCallback, [], '<span class="monospace">(name)</span> sets a preset by name for the current API', true, true);
$(document).on('click', '[data-preset-manager-update]', async function () {
const apiId = $(this).data('preset-manager-update');
const presetManager = getPresetManager(apiId);
@ -440,7 +558,7 @@ jQuery(async () => {
saveSettingsDebounced();
});
$(document).on('click', '[data-preset-manager-restore]', async function() {
$(document).on('click', '[data-preset-manager-restore]', async function () {
const apiId = $(this).data('preset-manager-restore');
const presetManager = getPresetManager(apiId);
@ -490,4 +608,4 @@ jQuery(async () => {
toastr.success('Preset restored');
}
});
});
}